UI updates (#15)

* Refactor technology stack integration and update documentation links

- Updated the technology stack links in various components to point to the new GitHub documentation repository.
- Refactored the Lambda Prize page to utilize a shared TechStackSection instead of local tech cards.
- Removed redundant tech stack message copies from the storage and technology stack pages.
- Enhanced the home feature cards section to use Link components for navigation.
- Added tests to ensure the integrity of the new tech stack structure and documentation links.

* feat: integrate social proof stats into home page and update related components
This commit is contained in:
Jinho Jang
2026-05-27 13:26:53 +09:00
committed by GitHub
parent adfc8435ad
commit cdc110a0ac
33 changed files with 569 additions and 491 deletions
@@ -7,7 +7,6 @@ import Image from 'next/image'
import { getTranslations } from 'next-intl/server'
import {
CardInfo,
Footer,
GiantSwitch,
GiantSwitchTag,
@@ -17,155 +16,28 @@ import {
TableRow,
ViewToggle,
} from '@acid-info/logos-ui'
import { Button, Card } from '@/components/ui'
import type { Language } from '@repo/content/schemas'
import { TechStackDiagram } from '@/components/sections/shared/tech-stack-diagram'
import { Button } from '@/components/ui'
import { EXTERNAL_URLS, ROUTES } from '@/constants/routes'
const cardImages = {
storage: '/design-systems/storage.png',
messaging: '/design-systems/messaging.png',
blockchain: '/design-systems/blockchain.png',
userModules: '/design-systems/user-modules.png',
networking: '/design-systems/networking.png',
kernel: '/design-systems/kernel.png',
} as const
function Thumb({ src, alt }: { src: string; alt: string }) {
return <Image src={src} alt={alt} width={46} height={57} />
}
import { getHomeTechStackOverview } from '@/lib/tech-stack-overview'
// --- Cards --------------------------------------------------------------
function CardGrid({ state }: { state: 'default' | 'hover' }) {
const isHover = state === 'hover'
const networkingTitle = (
<>
<span className="block">The Networking Stack:</span>
<span className="block">Discovery, Peering, and Mix-Net</span>
</>
)
export async function Cards({ locale }: { locale: Language }) {
const techStack = await getHomeTechStackOverview(locale)
return (
<div className="flex flex-col gap-[12px]">
{/* Row 1: four small cards */}
<div className="grid grid-cols-1 gap-[12px] sm:grid-cols-2 lg:grid-cols-4">
<Card
height={366}
forceHover={isHover}
staticDefault={!isHover}
image={isHover && <Thumb src={cardImages.storage} alt="" />}
title="Storage"
description={
isHover
? 'Decentralised file storage and retrieval, using content-addressed (CID-based) data'
: undefined
}
ctaHref={isHover ? ROUTES.storage : undefined}
/>
<Card
height={366}
forceHover={isHover}
staticDefault={!isHover}
image={isHover && <Thumb src={cardImages.messaging} alt="" />}
title="Messaging"
description={
isHover
? 'Private, censorship-resistant communication between parties.'
: undefined
}
ctaHref={isHover ? ROUTES.messaging : undefined}
/>
<Card
height={366}
forceHover={isHover}
staticDefault={!isHover}
image={isHover && <Thumb src={cardImages.blockchain} alt="" />}
title="Blockchain"
description={
isHover ? 'Decentralised compute and consensus.' : undefined
}
ctaHref={isHover ? ROUTES.blockchain : undefined}
>
{isHover && (
<>
<CardInfo
height={78}
label="Logos Execution Zone (LEZ)"
description="Developers can deploy programs, run AMMs, transfer tokens, and build financial primitives with built-in privacy."
/>
<CardInfo
height={78}
label="Data Availability and Consensus: Cryptarchia"
description="A private proof-of-stake consensus mechanism where validator identities and stake amounts remain hidden."
/>
</>
)}
</Card>
<Card
height={366}
forceHover={isHover}
staticDefault={!isHover}
image={isHover && <Thumb src={cardImages.userModules} alt="" />}
title="User Modules"
description={
isHover
? 'Anyone can build modules that plug into the same IPC infrastructure.'
: undefined
}
ctaHref={isHover ? ROUTES.technologyStack : undefined}
/>
</div>
{/* Row 2: wide band — Networking Stack (no Lambda icon glyph in Figma) */}
<Card
height={196}
forceHover={isHover}
staticDefault={!isHover}
showIcon={false}
image={isHover && <Thumb src={cardImages.networking} alt="" />}
title={networkingTitle}
description={
isHover
? 'This layer handles how Logos nodes find each other, establish connections, and communicate.'
: undefined
}
ctaHref={isHover ? ROUTES.networking : undefined}
<div className="flex w-full flex-col gap-[32px] bg-white p-[20px]">
<h2 className="font-display text-[64px] leading-[1] tracking-[-0.03em] text-brand-dark-green">
Cards
</h2>
<TechStackDiagram
data={techStack}
networkingHref={ROUTES.networking}
foundationHref={ROUTES.technologyStack}
/>
{/* Row 3: wide band — Foundation Kernel (no Lambda icon glyph in Figma) */}
<Card
height={196}
forceHover={isHover}
staticDefault={!isHover}
showIcon={false}
image={isHover && <Thumb src={cardImages.kernel} alt="" />}
title="The Foundation: Logos Kernel"
description={
isHover
? 'A microkernel that handles the essential primitives every decentralised application needs.'
: undefined
}
ctaHref={isHover ? ROUTES.technologyStack : undefined}
/>
</div>
)
}
export function Cards() {
return (
<div className="flex w-full flex-col gap-[80px] bg-white p-[20px]">
<div className="flex flex-col gap-[32px]">
<h2 className="font-display text-[64px] leading-[1] tracking-[-0.03em] text-brand-dark-green">
Default
</h2>
<CardGrid state="default" />
</div>
<div className="flex flex-col gap-[32px]">
<h2 className="font-display text-[64px] leading-[1] tracking-[-0.03em] text-brand-dark-green">
Hover
</h2>
<CardGrid state="hover" />
</div>
</div>
)
}
@@ -183,7 +55,7 @@ export function Buttons() {
<p className="font-mono text-[10px] leading-[1.3] font-medium text-brand-dark-green uppercase opacity-50">
Primary
</p>
<Button variant="primary" href={ROUTES.technologyStack}>
<Button variant="primary" href={EXTERNAL_URLS.docs}>
View The Docs
</Button>
</div>
@@ -191,7 +63,7 @@ export function Buttons() {
<p className="font-mono text-[10px] leading-[1.3] font-medium text-brand-dark-green uppercase opacity-50">
Secondary
</p>
<Button variant="secondary" href={ROUTES.technologyStack}>
<Button variant="secondary" href={EXTERNAL_URLS.docs}>
View The Docs
</Button>
</div>
@@ -199,7 +71,7 @@ export function Buttons() {
<p className="font-mono text-[10px] leading-[1.3] font-medium text-brand-dark-green uppercase opacity-50">
Tertiary
</p>
<Button variant="tertiary" href={ROUTES.technologyStack}>
<Button variant="tertiary" href={EXTERNAL_URLS.docs}>
View The Docs
</Button>
</div>
@@ -207,7 +79,7 @@ export function Buttons() {
<p className="font-mono text-[10px] leading-[1.3] font-medium text-brand-dark-green uppercase opacity-50">
Link
</p>
<Button variant="link" href={ROUTES.technologyStack}>
<Button variant="link" href={EXTERNAL_URLS.docs}>
View The Docs
</Button>
</div>
+13 -2
View File
@@ -7,6 +7,8 @@
* - components.tsx → Cards / Buttons / Tables / GiantSwitches /
* ViewToggles / Paginations / Footers
*/
import { isActiveLocale } from '@repo/content/locales'
import { createDefaultMetadata } from '@/lib/metadata'
import { ColorPalette } from './_sections/color-palette'
@@ -37,12 +39,21 @@ export async function generateMetadata({
})
}
export default function DesignSystemsPage() {
export default async function DesignSystemsPage({
params,
}: {
params: Promise<{ locale: string }>
}) {
const { locale } = await params
if (!isActiveLocale(locale)) {
throw new Error(`DesignSystemsPage received non-active locale "${locale}"`)
}
return (
<div className="mx-auto flex w-full max-w-[1440px] flex-col gap-[40px] py-10">
<ColorPalette />
<TypeStyles />
<Cards />
<Cards locale={locale} />
<Buttons />
<Tables />
<GiantSwitches />
+4 -4
View File
@@ -11,10 +11,10 @@ const NAMESPACE = 'pages.getStarted'
const docs = [
{ key: 'architecture', href: EXTERNAL_URLS.docs },
{ key: 'blockchain', href: `${EXTERNAL_URLS.docs}/logos-stack/blockchain` },
{ key: 'messaging', href: `${EXTERNAL_URLS.docs}/logos-stack/messaging` },
{ key: 'storage', href: `${EXTERNAL_URLS.docs}/logos-stack/storage` },
{ key: 'api', href: `${EXTERNAL_URLS.docs}/api` },
{ key: 'blockchain', href: EXTERNAL_URLS.docs },
{ key: 'messaging', href: EXTERNAL_URLS.docs },
{ key: 'storage', href: EXTERNAL_URLS.docs },
{ key: 'api', href: EXTERNAL_URLS.docs },
] as const
const communityLinks = [
@@ -1,10 +1,11 @@
import Image from 'next/image'
import { LogosMark } from '@acid-info/logos-ui'
import type { TechStackOverviewSection } from '@repo/content/schemas'
import TechStackSection from '@/components/sections/home/tech-stack-section'
import { Button, ButtonArrowIcon } from '@/components/ui'
import { ROUTES } from '@/constants/routes'
import { Link } from '@/i18n/navigation'
import { DownloadIcon } from '@/components/sections/shared/builder-cta-card'
interface RowCopy {
label: string
@@ -18,17 +19,6 @@ interface PrizeCopy {
status: string
}
interface TechCardCopy {
title: string
body: string
badges?: string[]
}
interface TechWideCopy {
title: string
body: string
}
interface SupportRowCopy extends RowCopy {
action: string
}
@@ -62,22 +52,6 @@ interface LambdaPrizePageCopy {
secondaryCta: string
rows: RowCopy[]
}
techStack: {
disclaimer: string
heading: string
body: string
cta: string
basecamp: {
title: string
body: string
cta: string
}
cards: TechCardCopy[]
networking: TechWideCopy
foundation: TechWideCopy
primaryCta: string
secondaryCta: string
}
support: {
heading: string
body: string
@@ -88,6 +62,7 @@ interface LambdaPrizePageCopy {
interface LambdaPrizePageProps {
copy: LambdaPrizePageCopy
techStack: TechStackOverviewSection
}
const prizeImages = [
@@ -327,113 +302,6 @@ function AboutProgramme({ copy }: { copy: LambdaPrizePageCopy['about'] }) {
)
}
function TechCard({ card }: { card: TechCardCopy }) {
return (
<div className="flex h-[258px] items-center justify-center rounded-3xl border border-brand-dark-green p-3 text-center md:h-[366px]">
<div className="flex w-full flex-col items-center gap-3">
<LogosMark size={18} className="hidden md:block" />
<h3 className="text-subhead-sans">{card.title}</h3>
<p className="text-mono-s max-w-[208px] text-center">{card.body}</p>
{card.badges ? (
<div className="mt-2 flex w-full max-w-[164px] flex-col gap-1.5">
{card.badges.map((badge) => (
<div
key={badge}
className="rounded-[18px] border border-brand-dark-green/50 px-2 py-2 text-center font-mono text-[10px] font-medium uppercase leading-[1.3]"
>
{badge}
</div>
))}
</div>
) : null}
</div>
</div>
)
}
function WideTechCard({
title,
body,
className = '',
}: TechWideCopy & { className?: string }) {
return (
<div
className={`flex h-[196px] items-center justify-center rounded-3xl border border-brand-dark-green p-3 text-center ${className}`}
>
<div className="flex flex-col items-center gap-3">
<h3 className="text-subhead-sans max-w-[320px] whitespace-pre-line">
{title}
</h3>
<p className="text-mono-s max-w-[208px] text-center">{body}</p>
</div>
</div>
)
}
function TechStack({ copy }: { copy: LambdaPrizePageCopy['techStack'] }) {
const headingLines = copy.heading.split('\n')
return (
<section className="mt-[100px] h-[1548px] bg-brand-off-white px-3 pt-3 text-brand-dark-green md:mt-0 md:h-[1507px]">
<div className="flex items-start justify-between">
<p className="text-mono-s w-[226px]">{copy.disclaimer}</p>
<div className="flex flex-col gap-1 md:flex-row">
<Button href={ROUTES.technologyStack} className="cursor-pointer">
{copy.primaryCta}
</Button>
<Button
href={ROUTES.buildersHub}
variant="secondary"
className="cursor-pointer"
>
{copy.secondaryCta}
</Button>
</div>
</div>
<div className="mx-auto mt-[70px] flex w-full max-w-[464px] flex-col items-center text-center md:mt-[52px]">
<h2 className="font-display text-[40px] leading-none tracking-[-0.03em] md:text-[56px] md:leading-[0.88]">
{headingLines.map((line) => (
<span key={line} className="block">
{line}
</span>
))}
</h2>
<p className="text-mono-s mt-10 w-[359px] max-w-full">{copy.body}</p>
<div className="mt-6">
<TertiaryCta href={ROUTES.technologyStack}>{copy.cta}</TertiaryCta>
</div>
</div>
<div className="mt-[100px] flex flex-col gap-3">
<div className="flex h-[124px] items-center justify-between rounded-3xl border border-brand-dark-green p-3 md:h-[196px]">
<div className="flex flex-col gap-3 md:mx-auto md:items-center">
<div className="flex items-center gap-2">
<LogosMark size={14} />
<h3 className="text-subhead-sans">{copy.basecamp.title}</h3>
</div>
<p className="text-mono-s w-[208px] md:hidden">
{copy.basecamp.body}
</p>
</div>
<Button
href={ROUTES.basecamp}
icon={<DownloadIcon />}
className="cursor-pointer"
>
{copy.basecamp.cta}
</Button>
</div>
<div className="grid grid-cols-2 gap-3 md:grid-cols-4">
{copy.cards.map((card) => (
<TechCard key={card.title} card={card} />
))}
</div>
<WideTechCard {...copy.networking} />
<WideTechCard {...copy.foundation} />
</div>
</section>
)
}
function Support({ copy }: { copy: LambdaPrizePageCopy['support'] }) {
return (
<section className="h-[406px] border-t border-brand-dark-green/10 bg-brand-off-white pt-10 text-brand-dark-green md:h-[421px]">
@@ -469,14 +337,18 @@ function Support({ copy }: { copy: LambdaPrizePageCopy['support'] }) {
)
}
export function LambdaPrizePage({ copy }: LambdaPrizePageProps) {
export function LambdaPrizePage({ copy, techStack }: LambdaPrizePageProps) {
return (
<main className="overflow-hidden bg-brand-off-white">
<Hero copy={copy.hero} />
<HowItWorks copy={copy.howItWorks} evaluation={copy.evaluation} />
<FeaturedPrizes copy={copy.featured} />
<AboutProgramme copy={copy.about} />
<TechStack copy={copy.techStack} />
<TechStackSection
data={techStack}
networkingHref={ROUTES.networking}
foundationHref={ROUTES.technologyStack}
/>
<Support copy={copy.support} />
</main>
)
+8 -43
View File
@@ -1,6 +1,9 @@
import { getTranslations } from 'next-intl/server'
import { isActiveLocale } from '@repo/content/locales'
import { ROUTES } from '@/constants/routes'
import { getHomeTechStackOverview } from '@/lib/tech-stack-overview'
import { createTranslatedPageMetadata } from '@/lib/translated-page-metadata'
import { LambdaPrizePage } from './_sections/lambda-prize-page'
@@ -18,10 +21,15 @@ export default async function Page({
params: Promise<{ locale: string }>
}) {
const { locale } = await params
if (!isActiveLocale(locale)) {
throw new Error(`LambdaPrizePage received non-active locale "${locale}"`)
}
const t = await getTranslations({ locale, namespace: NAMESPACE })
const techStack = await getHomeTechStackOverview(locale)
return (
<LambdaPrizePage
techStack={techStack}
copy={{
hero: {
label: t('hero.label'),
@@ -129,49 +137,6 @@ export default async function Page({
},
],
},
techStack: {
disclaimer: t('techStack.disclaimer'),
heading: t('techStack.heading'),
body: t('techStack.body'),
cta: t('techStack.cta'),
basecamp: {
title: t('techStack.basecamp.title'),
body: t('techStack.basecamp.body'),
cta: t('techStack.basecamp.cta'),
},
cards: [
{
title: t('techStack.cards.storage.title'),
body: t('techStack.cards.storage.body'),
},
{
title: t('techStack.cards.messaging.title'),
body: t('techStack.cards.messaging.body'),
},
{
title: t('techStack.cards.blockchain.title'),
body: t('techStack.cards.blockchain.body'),
badges: [
t('techStack.cards.blockchain.badges.1'),
t('techStack.cards.blockchain.badges.2'),
],
},
{
title: t('techStack.cards.userModules.title'),
body: t('techStack.cards.userModules.body'),
},
],
networking: {
title: t('techStack.networking.title'),
body: t('techStack.networking.body'),
},
foundation: {
title: t('techStack.foundation.title'),
body: t('techStack.foundation.body'),
},
primaryCta: t('techStack.primaryCta'),
secondaryCta: t('techStack.secondaryCta'),
},
support: {
heading: t('support.heading'),
body: t('support.body'),
+11 -2
View File
@@ -23,6 +23,7 @@ import { ROUTES } from '@/constants/routes'
import { createPageMetadata } from '@/lib/page-metadata'
import { createSectionFinder } from '@/lib/page-sections'
import { getLatestPressArticles } from '@/lib/press-engine'
import { getSocialProofStats } from '@/lib/social-proof-stats'
const findSection = createSectionFinder('home')
@@ -42,38 +43,46 @@ export default async function HomePage({
const page = await getPageCopy(ROUTE, locale)
const hero = findSection<HeroSection>(page.sections, 'hero', 'home.atf')
const techStack = findSection<TechStackOverviewSection>(
page.sections,
'techStackOverview',
'home.techStack'
)
const parallelSocietyHeadline = findSection<FeaturedTextSection>(
page.sections,
'featuredText',
'home.parallelSocietyHeadline'
)
const parallelSocietyGallery = findSection<GallerySection>(
page.sections,
'gallery',
'home.parallelSociety'
)
const press = findSection<RelatedArticlesSection>(
page.sections,
'relatedArticles',
'home.press'
)
const circlesCta = findSection<FeaturedTextSection>(
page.sections,
'featuredText',
'home.circlesCta'
)
const articles = await getLatestPressArticles(press.visibleCount ?? 4)
const [articles, socialProofStats] = await Promise.all([
getLatestPressArticles(press.visibleCount ?? 4),
getSocialProofStats(),
])
return (
<>
<HeroSectionView data={hero} />
<SocialProofSection />
<SocialProofSection stats={socialProofStats} />
<AboutSection locale={locale} />
<BuilderPortalSection locale={locale} />
<FeatureCardsSection />
@@ -33,10 +33,8 @@ const findSection = createSectionFinder('messaging')
* - messaging.builderCta → MessagingBuilderCta
* - messaging.relatedArticles → MessagingRelatedArticles
*
* `MessagingTechStack` is deferred (same rationale as `StorageTechStack`):
* it composes the global techStackOverview with messaging-specific intro
* copy. Migration awaits either a cross-page shared partial or extra fields
* on `techStackOverview`.
* `MessagingTechStack` composes the shared tech stack explorer so the card
* grid stays aligned with `/technology-stack`.
*/
export default async function MessagingPage({
params,
@@ -34,11 +34,8 @@ const findSection = createSectionFinder('storage')
* - storage.builderCta → StorageBuilderCta
* - storage.relatedArticles → StorageRelatedArticles
*
* `StorageTechStack` is deferred: it composes the global techStackOverview
* (shared with `/technology-stack`) with a storage-specific intro and per-card
* body text (`pages.storage.techStack.*`). Migrating cleanly needs either a
* cross-page shared partial or extra fields on `techStackOverview`. Until then
* the component continues to read from `messages.en.json`.
* `StorageTechStack` composes the shared tech stack explorer so the card grid
* stays aligned with `/technology-stack`.
*/
export default async function StoragePage({
params,
@@ -19,4 +19,17 @@ describe('broadcast network page contract', () => {
expect(source).toContain('h-[107px] w-[190px]')
expect(source).not.toContain('size-[107px]')
})
test('header background matches the press audio page tone', () => {
const source = readComponent(
'../components/site-header/site-header-client.tsx'
)
expect(source).toContain('usesAccentTanHeaderTone')
expect(source).toContain('ROUTES.logosBroadcastNetwork')
expect(source).toContain('ROUTES.podcast')
expect(source).toContain(
"usesAccentTanHeaderTone && 'bg-accent-tan'"
)
})
})
@@ -0,0 +1,24 @@
import { readFileSync } from 'node:fs'
import { fileURLToPath } from 'node:url'
import { describe, expect, test } from 'vitest'
function readAppFile(path: string) {
return readFileSync(fileURLToPath(new URL(`../${path}`, import.meta.url)), {
encoding: 'utf8',
})
}
describe('home feature cards contract', () => {
test('uses the whole path card as the link target', () => {
const source = readAppFile(
'../components/sections/home/feature-cards-section.tsx'
)
expect(source).toContain("import { Link } from '@/i18n/navigation'")
expect(source).toContain('<Link')
expect(source).toContain('href={card.href}')
expect(source).toContain('cursor-pointer')
expect(source).not.toContain('<Button\n href={card.href}')
})
})
@@ -0,0 +1,54 @@
import { readFileSync } from 'node:fs'
import { fileURLToPath } from 'node:url'
import { describe, expect, test } from 'vitest'
function readAppFile(path: string) {
return readFileSync(fileURLToPath(new URL(`../${path}`, import.meta.url)), {
encoding: 'utf8',
})
}
describe('lambda prize technology stack contract', () => {
test('reuses the home technology stack section instead of local cards', () => {
const page = readAppFile(
'../app/[locale]/lambda-prize/_sections/lambda-prize-page.tsx'
)
expect(page).toContain(
"import TechStackSection from '@/components/sections/home/tech-stack-section'"
)
expect(page).not.toContain('function TechCard')
expect(page).not.toContain('function WideTechCard')
expect(page).not.toContain('interface TechCardCopy')
})
test('keeps the design systems card sample on the shared diagram', () => {
const page = readAppFile(
'../app/[locale]/design-systems/_sections/components.tsx'
)
expect(page).toContain(
"import { TechStackDiagram } from '@/components/sections/shared/tech-stack-diagram'"
)
expect(page).not.toContain('function CardGrid')
expect(page).not.toContain('const cardImages')
expect(page).not.toContain('The Foundation: Logos Kernel')
})
test('does not keep page-local technology stack message copies', () => {
const messages = JSON.parse(readAppFile('../messages/en.json')) as {
pages: {
storage?: { techStack?: unknown }
technologyStack: { stack: Record<string, unknown> }
}
}
expect(messages.pages.storage?.techStack).toBeUndefined()
expect(Object.keys(messages.pages.technologyStack.stack).sort()).toEqual([
'body',
'titleLine1',
'titleLine2',
])
})
})
@@ -5,8 +5,9 @@ import { useTranslations } from 'next-intl'
import { LogosMark } from '@acid-info/logos-ui'
import { Button } from '@/components/ui'
import { ButtonArrowIcon } from '@/components/ui'
import { ROUTES } from '@/constants/routes'
import { Link } from '@/i18n/navigation'
interface PathCard {
key: 'build' | 'operate' | 'activism'
@@ -21,8 +22,9 @@ interface PathCard {
function PathCardView({ card }: { card: PathCard }) {
return (
<article
className="group/path-card relative h-[422px] overflow-hidden rounded-3xl bg-brand-dark-green text-brand-off-white"
<Link
href={card.href}
className="group/path-card relative block h-[422px] cursor-pointer overflow-hidden rounded-3xl bg-brand-dark-green text-brand-off-white focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-brand-dark-green"
>
<Image
src={card.image}
@@ -40,17 +42,21 @@ function PathCardView({ card }: { card: PathCard }) {
<h3 className="text-h3-serif">{card.title}</h3>
</div>
<Button
href={card.href}
className="absolute top-3 right-3 cursor-pointer bg-brand-off-white text-brand-dark-green backdrop-blur-[5px] transition-colors duration-300 ease-out hover:bg-accent-steel-teal focus-visible:bg-accent-steel-teal group-hover/path-card:bg-accent-steel-teal"
<span
className="absolute top-3 right-3 inline-flex items-center justify-center rounded-xl bg-brand-off-white px-3 py-2 text-brand-dark-green backdrop-blur-[5px] transition-colors duration-300 ease-out group-hover/path-card:bg-accent-steel-teal group-focus-visible/path-card:bg-accent-steel-teal"
>
{card.cta}
</Button>
<span className="inline-flex items-center gap-1">
<span className="font-mono text-[10px] leading-[1.35] font-semibold whitespace-nowrap uppercase">
{card.cta}
</span>
<ButtonArrowIcon />
</span>
</span>
<p className="absolute right-6 bottom-6 left-6 font-sans text-[14px] leading-[1.2] font-medium">
{card.body}
</p>
</article>
</Link>
)
}
@@ -3,8 +3,10 @@
import Image from 'next/image'
import { useTranslations } from 'next-intl'
import type { SocialProofStats } from '@/lib/social-proof-stats'
interface SocialProofCard {
key: 'contributions' | 'nodes' | 'circles' | 'issues'
key: 'contributions' | 'contributors' | 'repositories' | 'circles'
value: string
label: string
body: string
@@ -49,13 +51,17 @@ function SocialProofCardView({
)
}
export default function SocialProofSection() {
interface SocialProofSectionProps {
stats: SocialProofStats
}
export default function SocialProofSection({ stats }: SocialProofSectionProps) {
const t = useTranslations('home.socialProof')
const cards: SocialProofCard[] = [
{
key: 'contributions',
value: t('contributions.value'),
value: stats.contributions,
label: t('contributions.label'),
body: t('contributions.body'),
image: '/images/home/figma-refresh/social-contributions.webp',
@@ -63,31 +69,31 @@ export default function SocialProofSection() {
imageClassName: 'object-[47%_52%]',
},
{
key: 'nodes',
value: t('nodes.value'),
label: t('nodes.label'),
body: t('nodes.body'),
key: 'contributors',
value: stats.contributors,
label: t('contributors.label'),
body: t('contributors.body'),
image: '/images/home/figma-refresh/social-node.webp',
mobileClassName: 'h-[432px]',
imageClassName: 'object-[50%_52%]',
},
{
key: 'repositories',
value: stats.repositories,
label: t('repositories.label'),
body: t('repositories.body'),
image: '/images/home/figma-refresh/social-issues.webp',
mobileClassName: 'h-[411px]',
imageClassName: 'object-[47%_51%]',
},
{
key: 'circles',
value: t('circles.value'),
value: stats.circles,
label: t('circles.label'),
body: t('circles.body'),
image: '/images/home/figma-refresh/social-circles.webp',
mobileClassName: 'h-[411px]',
imageClassName: 'object-[50%_50%]',
},
{
key: 'issues',
value: t('issues.value'),
label: t('issues.label'),
body: t('issues.body'),
image: '/images/home/figma-refresh/social-issues.webp',
mobileClassName: 'h-[432px]',
imageClassName: 'object-[47%_51%]',
imageClassName: 'object-[50%_50%]',
},
]
@@ -2,7 +2,7 @@ import { getTranslations } from 'next-intl/server'
import Image from 'next/image'
import { Button } from '@/components/ui'
import { ROUTES } from '@/constants/routes'
import { EXTERNAL_URLS, ROUTES } from '@/constants/routes'
interface SupportCard {
title: string
@@ -38,7 +38,7 @@ export default async function StartBuildingSection({
{ title: t('lambdaPrize'), href: ROUTES.lambdaPrize, cta: t('cardCta') },
{ title: t('rfps'), href: ROUTES.rfps, cta: t('cardCta') },
{ title: t('ideas'), href: ROUTES.ideas, cta: t('cardCta') },
{ title: t('docs'), href: ROUTES.technologyStack, cta: t('cardCta') },
{ title: t('docs'), href: EXTERNAL_URLS.docs, cta: t('cardCta') },
]
return (
@@ -81,6 +81,9 @@ export default function SiteHeaderClient({
normalizedPathname.endsWith(ROUTES.lambdaPrize)
const usesTransparentHeader = normalizedPathname.endsWith(ROUTES.press)
const usesOverlayHeader = usesHeroHeaderTone || usesTransparentHeader
const usesAccentTanHeaderTone =
normalizedPathname.endsWith(ROUTES.logosBroadcastNetwork) ||
normalizedPathname.endsWith(ROUTES.podcast)
const open = () => {
setInitialPanelLabel(null)
setIsOpen(true)
@@ -135,7 +138,8 @@ export default function SiteHeaderClient({
<header
className={clsx(
'left-0 right-0 top-0 z-50',
usesOverlayHeader ? 'fixed' : 'sticky'
usesOverlayHeader ? 'fixed' : 'sticky',
usesAccentTanHeaderTone && 'bg-accent-tan'
)}
>
<div
+1 -1
View File
@@ -90,7 +90,7 @@ export const EXTERNAL_URLS = {
// Forum
forum: 'https://forum.logos.co/',
docs: 'https://docs.logos.co',
docs: 'https://github.com/logos-co/logos-docs',
// Research
vacp2p: 'https://vac.dev',
+49 -1
View File
@@ -3,8 +3,9 @@ import { join, relative } from 'node:path'
import { describe, expect, it } from 'vitest'
import { ROUTES } from '@/constants/routes'
import { EXTERNAL_URLS, ROUTES } from '@/constants/routes'
import buildersHubResources from '../../../../content/builders-hub/resources/en.json' with { type: 'json' }
import buildersHubSettings from '../../../../content/builders-hub/settings/en.json' with { type: 'json' }
import footer from '../../../../content/site/en/footer.json' with { type: 'json' }
import navigation from '../../../../content/site/en/navigation.json' with { type: 'json' }
@@ -33,6 +34,8 @@ const repoPressArticlePaths = [
].map((path) => join(repoRoot, path))
const jobsHref = 'https://free.technology/jobs'
const onboardingCalendarHref = 'https://cal.com/team/logos-onboarding/intro'
const logosDocsHref = 'https://github.com/logos-co/logos-docs'
const docsLabels = new Set(['docs', 'documentation', 'view the docs'])
const routeUsageAllowlist = new Set([
'apps/web/app/[locale]/work-with-us/page.tsx',
'apps/web/app/sitemap.ts',
@@ -40,6 +43,31 @@ const routeUsageAllowlist = new Set([
'apps/web/lib/__tests__/link-policy.test.ts',
])
const collectDocsLinks = (value: unknown): Array<{ label: string; href: string }> => {
if (Array.isArray(value)) {
return value.flatMap(collectDocsLinks)
}
if (!value || typeof value !== 'object') return []
const record = value as Record<string, unknown>
const docsText =
typeof record.label === 'string' && docsLabels.has(record.label.toLowerCase())
? record.label
: typeof record.title === 'string' &&
docsLabels.has(record.title.toLowerCase())
? record.title
: null
const ownLink =
docsText && typeof record.href === 'string'
? [{ label: docsText, href: record.href }]
: []
return ownLink.concat(
Object.values(record).flatMap((child) => collectDocsLinks(child))
)
}
const collectTextFiles = (dir: string): string[] => {
return readdirSync(dir).flatMap((entry) => {
const path = join(dir, entry)
@@ -148,6 +176,26 @@ describe('link policy', () => {
)
})
it('routes Docs and Documentation links to the Logos docs repository', () => {
const contentDocsLinks = [
footer,
navigation,
buildersHubResources,
buildersHubSettings,
...readdirSync(join(repoRoot, 'content/pages/en'))
.filter((entry) => entry.endsWith('.json'))
.map((entry) =>
JSON.parse(readFileSync(join(repoRoot, 'content/pages/en', entry), 'utf8'))
),
].flatMap(collectDocsLinks)
expect(EXTERNAL_URLS.docs).toBe(logosDocsHref)
expect(contentDocsLinks.length).toBeGreaterThan(0)
expect(contentDocsLinks).toEqual(
contentDocsLinks.map((link) => ({ ...link, href: logosDocsHref }))
)
})
it('does not use the retired Work With Us route for public CTAs', () => {
const offenders = scannedRoots.flatMap(collectTextFiles).flatMap((file) => {
const relativePath = relative(repoRoot, file)
@@ -0,0 +1,77 @@
import { afterEach, describe, expect, test, vi } from 'vitest'
import { getSocialProofStats } from '../social-proof-stats'
const jsonResponse = (payload: unknown): Response =>
({
ok: true,
status: 200,
json: async () => payload,
}) as unknown as Response
afterEach(() => {
vi.restoreAllMocks()
})
describe('getSocialProofStats', () => {
test('maps contribution and circle API totals for the home social proof cards', async () => {
const fetchMock = vi
.spyOn(globalThis, 'fetch')
.mockResolvedValueOnce(
jsonResponse({
stg_external_contributors_agg_total_ext_contributions: [
{
total_commits: 3441,
total_external_collaborators: 218,
total_repositories: 341,
},
],
})
)
.mockResolvedValueOnce(
jsonResponse({
data: {
stg_external_circle_circle_event_aggregate: {
aggregate: {
count: 34,
},
},
},
})
)
await expect(getSocialProofStats()).resolves.toEqual({
contributions: '3,441',
contributors: '218',
repositories: '341',
circles: '34',
})
expect(fetchMock).toHaveBeenCalledTimes(2)
expect(fetchMock).toHaveBeenNthCalledWith(
1,
'https://hasura.bi.status.im/api/rest/contributions/count_all',
{ cache: 'force-cache' }
)
expect(fetchMock).toHaveBeenNthCalledWith(
2,
'https://hasura.bi.status.im/v1/graphql',
expect.objectContaining({
method: 'POST',
cache: 'force-cache',
headers: { 'Content-Type': 'application/json' },
})
)
})
test('keeps the requested stats as build fallback when the APIs fail', async () => {
vi.spyOn(globalThis, 'fetch').mockRejectedValue(new Error('network down'))
await expect(getSocialProofStats()).resolves.toEqual({
contributions: '3,441',
contributors: '218',
repositories: '341',
circles: '34',
})
})
})
+5 -5
View File
@@ -1,11 +1,11 @@
import { getLatestEventsByCity } from './circle-events'
import {
BI_GRAPHQL_API_URL,
CIRCLES_GRAPHQL_RESPONSE_KEY,
LOGOS_GRAPHQL_API_URL,
} from './logos-data-api'
import { logger } from './logger'
const LOGOS_GRAPHQL_API_URL = 'https://api.logos.co/v1/graphql'
const BI_GRAPHQL_API_URL = 'https://hasura.bi.status.im/v1/graphql'
const CIRCLES_GRAPHQL_RESPONSE_KEY =
'stg_external_circle_circle_event_aggregate'
export const ACTIVE_CIRCLES_DAYS = 120
type GraphqlResponse<TData> = {
+2 -3
View File
@@ -1,7 +1,6 @@
import { LOGOS_GRAPHQL_API_URL } from './logos-data-api'
import { logger } from './logger'
const HASURA_URL = 'https://api.logos.co/v1/graphql'
const CIRCLE_EVENTS_QUERY = `query CircleEvents {
stg_external_circle_circle_event {
event_id
@@ -41,7 +40,7 @@ interface HasuraResponse {
*/
export async function fetchCircleEvents(): Promise<CircleEvent[]> {
try {
const response = await fetch(HASURA_URL, {
const response = await fetch(LOGOS_GRAPHQL_API_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query: CIRCLE_EVENTS_QUERY }),
+12
View File
@@ -0,0 +1,12 @@
export const LOGOS_GRAPHQL_API_URL = 'https://api.logos.co/v1/graphql'
export const BI_GRAPHQL_API_URL = 'https://hasura.bi.status.im/v1/graphql'
export const CONTRIBUTIONS_API_URL =
'https://hasura.bi.status.im/api/rest/contributions/count_all'
export const CONTRIBUTIONS_API_RESPONSE_KEY =
'stg_external_contributors_agg_total_ext_contributions'
export const CIRCLES_GRAPHQL_RESPONSE_KEY =
'stg_external_circle_circle_event_aggregate'
+131
View File
@@ -0,0 +1,131 @@
import {
BI_GRAPHQL_API_URL,
CIRCLES_GRAPHQL_RESPONSE_KEY,
CONTRIBUTIONS_API_RESPONSE_KEY,
CONTRIBUTIONS_API_URL,
} from './logos-data-api'
import { logger } from './logger'
const ACTIVE_CIRCLES_DAYS = 120
const FALLBACK_SOCIAL_PROOF_STATS = {
contributions: 3441,
contributors: 218,
repositories: 341,
circles: 34,
} as const
type ContributionsApiData = {
total_commits?: number | null
total_external_collaborators?: number | null
total_repositories?: number | null
}
type ContributionsApiResponse = {
[CONTRIBUTIONS_API_RESPONSE_KEY]?: ContributionsApiData[]
}
type CirclesApiResponse = {
data?: {
[CIRCLES_GRAPHQL_RESPONSE_KEY]?: {
aggregate?: {
count?: number | null
}
}
}
}
export interface SocialProofStats {
contributions: string
contributors: string
repositories: string
circles: string
}
function formatStat(
value: number | null | undefined,
fallback: number
): string {
return (value ?? fallback).toLocaleString('en-US')
}
function getTodayISODateDaysAgo(days: number): string {
const date = new Date()
date.setDate(date.getDate() - days)
return date.toISOString().split('T')[0] ?? ''
}
function getCirclesGraphqlQuery(dateString: string): string {
return `query CountDistinctCities {
${CIRCLES_GRAPHQL_RESPONSE_KEY}(where: { end_at: { _gte: "${dateString}" } }) {
aggregate {
count(distinct: true, columns: location_city)
}
}
}`
}
async function fetchContributionsData(): Promise<ContributionsApiData | null> {
try {
const response = await fetch(CONTRIBUTIONS_API_URL, {
cache: 'force-cache',
})
if (!response.ok) {
throw new Error(`Contributions request failed: ${response.status}`)
}
const data = (await response.json()) as ContributionsApiResponse
return data[CONTRIBUTIONS_API_RESPONSE_KEY]?.[0] ?? null
} catch (error) {
logger.error('Failed to fetch social proof contribution stats', { error })
return null
}
}
async function fetchTotalCircles(): Promise<number | null> {
try {
const activeSinceDate = getTodayISODateDaysAgo(ACTIVE_CIRCLES_DAYS)
const response = await fetch(BI_GRAPHQL_API_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
query: getCirclesGraphqlQuery(activeSinceDate),
}),
cache: 'force-cache',
})
if (!response.ok) {
throw new Error(`Circles request failed: ${response.status}`)
}
const data = (await response.json()) as CirclesApiResponse
return data.data?.[CIRCLES_GRAPHQL_RESPONSE_KEY]?.aggregate?.count ?? null
} catch (error) {
logger.error('Failed to fetch social proof circle stats', { error })
return null
}
}
export async function getSocialProofStats(): Promise<SocialProofStats> {
const [contributionsData, totalCircles] = await Promise.all([
fetchContributionsData(),
fetchTotalCircles(),
])
return {
contributions: formatStat(
contributionsData?.total_commits,
FALLBACK_SOCIAL_PROOF_STATS.contributions
),
contributors: formatStat(
contributionsData?.total_external_collaborators,
FALLBACK_SOCIAL_PROOF_STATS.contributors
),
repositories: formatStat(
contributionsData?.total_repositories,
FALLBACK_SOCIAL_PROOF_STATS.repositories
),
circles: formatStat(totalCircles, FALLBACK_SOCIAL_PROOF_STATS.circles),
}
}
+20
View File
@@ -0,0 +1,20 @@
import { getPageCopy } from '@repo/content/loaders'
import type { Language, TechStackOverviewSection } from '@repo/content/schemas'
import { ROUTES } from '@/constants/routes'
import { createSectionFinder } from './page-sections'
const findHomeSection = createSectionFinder('home')
export async function getHomeTechStackOverview(
locale: Language
): Promise<TechStackOverviewSection> {
const homePage = await getPageCopy(ROUTES.home, locale)
return findHomeSection<TechStackOverviewSection>(
homePage.sections,
'techStackOverview',
'home.techStack'
)
}
+8 -77
View File
@@ -38,24 +38,20 @@
},
"socialProof": {
"contributions": {
"value": "2262",
"label": "Contributions",
"label": "Total contributions",
"body": "An open source community of builders, researchers, and technologists."
},
"nodes": {
"value": "205",
"label": "Node Operators",
"contributors": {
"label": "Active contributors",
"body": "An open source community of builders, researchers, and technologists."
},
"repositories": {
"label": "Repositories",
"body": "Open source tools, libraries, and infrastructure to inspect, fork, and build on."
},
"circles": {
"value": "26",
"label": "Circles",
"body": "Local chapters of activists and change seekers solving real world issues."
},
"issues": {
"value": "26",
"label": "Winnable Issues",
"body": "Local issues that Circles identify and solve from privacy tech to community funding."
}
},
"build": {
@@ -305,29 +301,7 @@
"stack": {
"titleLine1": "Explore the Logos",
"titleLine2": "Technology Stack.",
"body": "The Logos technology stack is a unified ecosystem of distinct modules. The entire stack is modular so developers can assemble their own Logos-based platform with a different selection of modules.",
"storage": "Storage",
"messaging": "Messaging",
"blockchain": "Blockchain",
"userModules": "User Modules",
"basecamp": "Basecamp",
"basecampBody": "Lorem ipsum dolor sit amet consectetur lorem ipsum dolor sit amet consectetur lorem.",
"basecampCta": "Install",
"networkingLine1": "The Networking Stack:",
"networkingLine2": "Discovery, Peering, and Mix-Net",
"foundation": "The Foundation: Logos Kernel"
}
},
"storage": {
"techStack": {
"storageBody": "Decentralised file storage and retrieval, using content-addressed (CID-based) data",
"messagingBody": "Private, censorship-resistant communication between parties.",
"blockchainBody": "Decentralised compute and consensus.",
"userModulesBody": "Anyone can build modules that plug into the same IPC infrastructure.",
"networkingBody": "This layer handles how Logos nodes find each other, establish connections, and communicate.",
"foundationBody": "A microkernel that handles the essential primitives every decentralised application needs.",
"lez": "LOGOS Execution zone (LEZ)",
"cryptarchia": "Data Availability and Consensus: Cryptarchia"
"body": "The Logos technology stack is a unified ecosystem of distinct modules. The entire stack is modular so developers can assemble their own Logos-based platform with a different selection of modules."
}
},
"ideas": {
@@ -447,49 +421,6 @@
}
}
},
"techStack": {
"disclaimer": "Disclaimer: This diagram oversimplifies the stack.",
"heading": "The Logos\nTechnology Stack",
"body": "Logos provides developers with a modular, plugin-based runtime for building decentralised applications.",
"cta": "See the stack",
"primaryCta": "Documentation",
"secondaryCta": "Builder Hub",
"basecamp": {
"title": "Basecamp",
"body": "Basecamp bundles the Logos stack into a practical starting point for builders.",
"cta": "Install"
},
"cards": {
"storage": {
"title": "Storage",
"body": "Decentralised file storage and retrieval, using content-addressed (CID-based) data"
},
"messaging": {
"title": "Messaging",
"body": "Private, censorship-resistant communication between parties."
},
"blockchain": {
"title": "Blockchain",
"body": "Decentralised compute and consensus.",
"badges": {
"1": "LOGOS Execution zone (LEZ)",
"2": "Data Availability and Consensus: Cryptarchia"
}
},
"userModules": {
"title": "User Modules",
"body": "Anyone can build modules that plug into the same IPC infrastructure."
}
},
"networking": {
"title": "The Networking Stack:\nDiscovery, Peering, and Mix-Net",
"body": "This layer handles how Logos nodes find each other, establish connections, and communicate."
},
"foundation": {
"title": "The Foundation: Logos Kernel",
"body": "A microkernel that handles the essential primitives every decentralised application needs."
}
},
"support": {
"heading": "Get Support",
"body": "Find examples, tutorials, and community channels for building on Logos.",
+1 -1
View File
@@ -15,7 +15,7 @@
"title": "Documentation",
"description": "Reference for the kernel, modules, and UI packages.",
"ctaLabel": "Browse",
"href": "/technology-stack",
"href": "https://github.com/logos-co/logos-docs",
"status": "published"
},
{
+5 -1
View File
@@ -15,7 +15,11 @@
"href": "https://discord.gg/logosnetwork",
"external": true
},
{ "label": "View the Docs", "href": "/technology-stack" }
{
"label": "View the Docs",
"href": "https://github.com/logos-co/logos-docs",
"external": true
}
]
},
"journey": {
+2 -2
View File
@@ -54,7 +54,7 @@
"description": "Basecamp is built around a modular architecture. At its core is a lightweight runtime that manages self-contained modules, each providing a distinct capability such as private peer-to-peer messaging or distributed storage.",
"cta": {
"label": "Documentation",
"href": "https://docs.logos.co",
"href": "https://github.com/logos-co/logos-docs",
"external": true,
"variant": "primary"
},
@@ -176,7 +176,7 @@
"description": "Basecamp is an open-source application. If you want to explore the codebase, build from source, or develop your own modules, everything lives on GitHub.",
"cta": {
"label": "View the docs",
"href": "https://docs.logos.co",
"href": "https://github.com/logos-co/logos-docs",
"external": true,
"variant": "primary"
}
+7 -2
View File
@@ -73,7 +73,11 @@
"key": "home.techStack",
"eyebrow": "A unified ecosystem. Private-by-default. Built for real life.",
"title": "The Logos Technology Stack",
"cta": { "label": "See the Stack", "href": "/technology-stack" },
"cta": {
"label": "See the Stack",
"href": "https://github.com/logos-co/logos-docs",
"external": true
},
"basecamp": {
"title": "Basecamp",
"body": "Lorem ipsum dolor sit amet consectetur lorem ipsum dolor sit amet consectetur lorem.",
@@ -132,7 +136,8 @@
"subheadingExtra": "Privacy-preserving blockchain for sovereign order and decentralised governance.",
"cta": {
"label": "View the Docs",
"href": "/technology-stack"
"href": "https://github.com/logos-co/logos-docs",
"external": true
},
"cards": [
{
@@ -55,7 +55,8 @@
},
{
"label": "Docs",
"href": "/technology-stack/blockchain",
"href": "https://github.com/logos-co/logos-docs",
"external": true,
"variant": "secondary"
}
]
@@ -68,7 +69,8 @@
"description": "Logos Blockchain is a proof-of-stake layer-1 with privacy built into the consensus mechanism itself — not bolted on at the application layer. Block proposers are anonymous. Transactions are private by default. The chain supports the deployment of application-specific execution environments called Sovereign Zones, connected through Mantle Channels for interoperability.",
"cta": {
"label": "View the docs",
"href": "/technology-stack/blockchain"
"href": "https://github.com/logos-co/logos-docs",
"external": true
}
},
{
@@ -79,7 +81,8 @@
"subheading": "A private proof-of-stake consensus mechanism where validator identities and stake amounts remain hidden.",
"cta": {
"label": "View the docs",
"href": "/technology-stack/blockchain"
"href": "https://github.com/logos-co/logos-docs",
"external": true
},
"cards": [
{
@@ -98,7 +101,8 @@
},
"secondaryCta": {
"label": "Docs",
"href": "/technology-stack/blockchain",
"href": "https://github.com/logos-co/logos-docs",
"external": true,
"variant": "secondary"
}
},
@@ -118,7 +122,8 @@
},
"secondaryCta": {
"label": "Docs",
"href": "/technology-stack/blockchain",
"href": "https://github.com/logos-co/logos-docs",
"external": true,
"variant": "secondary"
}
},
@@ -138,7 +143,8 @@
},
"secondaryCta": {
"label": "Docs",
"href": "/technology-stack/blockchain",
"href": "https://github.com/logos-co/logos-docs",
"external": true,
"variant": "secondary"
}
}
@@ -153,7 +159,8 @@
"description": "Lorem ipsum dolor sit amet consectetur",
"cta": {
"label": "View the docs",
"href": "/technology-stack"
"href": "https://github.com/logos-co/logos-docs",
"external": true
}
},
{
@@ -47,7 +47,8 @@
},
{
"label": "DOCS",
"href": "/technology-stack/messaging",
"href": "https://github.com/logos-co/logos-docs",
"external": true,
"variant": "secondary"
}
]
@@ -60,7 +61,8 @@
"description": "Logos Messaging provides the messaging layer for the Logos stack, letting applications send messages, relay data, and coordinate without relying on centralised servers or exposing metadata to surveillance.\n\nIt uses a peer-to-peer relay network in which messages propagate across nodes with no central broker, and it supports private one-to-one messaging, group messaging, and public broadcast channels.\n\nMessages can be encrypted end-to-end, and the relay network is designed to resist traffic analysis, making it difficult for observers to determine who is communicating with whom.",
"cta": {
"label": "View the docs",
"href": "/technology-stack/messaging"
"href": "https://github.com/logos-co/logos-docs",
"external": true
}
},
{
@@ -125,7 +127,8 @@
"description": "Lorem ipsum dolor sit amet consectetur",
"cta": {
"label": "View the docs",
"href": "/technology-stack"
"href": "https://github.com/logos-co/logos-docs",
"external": true
}
},
{
@@ -30,7 +30,8 @@
},
{
"label": "Docs",
"href": "/technology-stack/networking",
"href": "https://github.com/logos-co/logos-docs",
"external": true,
"variant": "secondary"
}
]
@@ -42,7 +43,8 @@
"description": "The networking layer handles how Logos nodes find each other, establish connections, and communicate. Private-by-default, this layer doesn't care whether modules above it are storing files, sending chat messages, or processing transactions. It provides private, reliable communication as a shared foundation.",
"cta": {
"label": "View the docs",
"href": "/technology-stack/networking"
"href": "https://github.com/logos-co/logos-docs",
"external": true
}
},
{
@@ -90,7 +92,8 @@
"description": "Reference for the kernel, modules, and UI packages.",
"cta": {
"label": "View the docs",
"href": "/technology-stack"
"href": "https://github.com/logos-co/logos-docs",
"external": true
}
},
{
@@ -50,7 +50,8 @@
},
{
"label": "DOCS",
"href": "/technology-stack/storage",
"href": "https://github.com/logos-co/logos-docs",
"external": true,
"variant": "secondary"
}
]
@@ -63,7 +64,8 @@
"description": "Files are addressed by content rather than by location. When you store a file, the network returns a CID — a content identifier derived from the file itself — and anyone with that CID can retrieve the file from whichever nodes hold it. There is no central server to depend on and no single host to fail. The usage is straightforward: store a file and get back a content identifier; provide a content identifier and get back the file. Storage works both inside the Logos App and from your own applications, through either the high-level module API or the lower-level libstorage library.",
"cta": {
"label": "View the docs",
"href": "/technology-stack/storage"
"href": "https://github.com/logos-co/logos-docs",
"external": true
}
},
{
@@ -140,7 +142,8 @@
"description": "Lorem ipsum dolor sit amet consectetur",
"cta": {
"label": "View the docs",
"href": "/technology-stack"
"href": "https://github.com/logos-co/logos-docs",
"external": true
}
},
{
+12 -8
View File
@@ -42,7 +42,8 @@
},
{
"label": "Documentation",
"href": "/technology-stack#open-source",
"href": "https://github.com/logos-co/logos-docs",
"external": true,
"variant": "secondary"
}
],
@@ -70,7 +71,8 @@
"eyebrow": "Disclaimer: Abstract representation of the stack.",
"cta": {
"label": "Docs",
"href": "/technology-stack#open-source"
"href": "https://github.com/logos-co/logos-docs",
"external": true
},
"architecture": {
"eyebrow": "Technology",
@@ -81,7 +83,8 @@
],
"cta": {
"label": "View the docs",
"href": "/technology-stack#stack"
"href": "https://github.com/logos-co/logos-docs",
"external": true
},
"image": {
"src": "/images/technology-stack/architecture-overview.jpg",
@@ -209,7 +212,7 @@
},
"secondaryCta": {
"label": "Docs",
"href": "https://docs.logos.co",
"href": "https://github.com/logos-co/logos-docs",
"external": true
}
},
@@ -224,7 +227,7 @@
},
"secondaryCta": {
"label": "Docs",
"href": "https://docs.waku.org",
"href": "https://github.com/logos-co/logos-docs",
"external": true
}
},
@@ -239,7 +242,7 @@
},
"secondaryCta": {
"label": "Docs",
"href": "https://docs.codex.storage",
"href": "https://github.com/logos-co/logos-docs",
"external": true
}
},
@@ -254,7 +257,7 @@
},
"secondaryCta": {
"label": "Docs",
"href": "https://vac.dev",
"href": "https://github.com/logos-co/logos-docs",
"external": true
}
}
@@ -268,7 +271,8 @@
"subheadingExtra": "Privacy-preserving blockchain for sovereign order and decentralised governance.",
"cta": {
"label": "View the Docs",
"href": "/technology-stack"
"href": "https://github.com/logos-co/logos-docs",
"external": true
},
"cards": [
{