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
14 changes: 7 additions & 7 deletions community-token/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,16 @@ Same pattern as the main site: meta tags are injected at build time (`scripts/ge
**Open Graph / Twitter image** — place your artwork here (not in git until you add it):

```
community-token/public/og/default.png
community-token/public/og/default.webp
```

| Requirement | Value |
| ----------- | ------------------------------------------------------------- |
| Format | **PNG** (real PNG bytes — not JPEG renamed) |
| Size | **1200 × 630** px |
| Served at | `https://community-token.feelyourprotocol.org/og/default.png` |
| Requirement | Value |
| ----------- | ----------------------------------------------------------------- |
| Format | **WebP** (1200×630; PNG/JPEG source OK — export as WebP for size) |
| Size | **1200 × 630** px |
| Served at | `https://community-token.feelyourprotocol.org/og/default.webp` |

Main site equivalent: `public/og/default.png` → `feelyourprotocol.org/og/default.png`. Each subdomain keeps its own copy under its own `public/` folder.
Main site equivalent: `public/og/default.webp` → `feelyourprotocol.org/og/default.webp`. Each subdomain keeps its own copy under its own `public/` folder.

Full deploy build (main site + community token + docs, rebuilt on server — `dist/` is not in git):

Expand Down
Binary file removed community-token/public/og/default.png
Binary file not shown.
Binary file added community-token/public/og/default.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion community-token/src/content/pageSeo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const CT_DESCRIPTION = truncateDescription(
)

/** Stable path under `community-token/public/og/` — copied to `dist/community-token/og/` on build. */
export const CT_OG_IMAGE_PATH = '/og/default.png' as const
export const CT_OG_IMAGE_PATH = '/og/default.webp' as const
export const CT_OG_IMAGE_WIDTH = 1200
export const CT_OG_IMAGE_HEIGHT = 630

Expand Down
6 changes: 3 additions & 3 deletions community-token/src/content/treasury.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export const ALLOCATION_CHART_2026: TreasuryChartSpec = {
chartId: 'treasury-allocation-2026',
title: 'Claimed fees (2026)',
subtitle: 'Earmarked use',
generatedAt: '2026-06-23',
generatedAt: '2026-06-24',
basis: {
description: 'Sum of claims in treasury/2026/claims.md',
totalEur: 2650,
Expand All @@ -71,7 +71,7 @@ export const ALLOCATION_CHART_2026: TreasuryChartSpec = {
{
id: 'work-jun',
label: 'June work',
eur: 1350,
eur: 1550,
color: '#06b6d4',
},
{
Expand All @@ -83,7 +83,7 @@ export const ALLOCATION_CHART_2026: TreasuryChartSpec = {
{
id: 'unallocated',
label: 'Unallocated',
eur: 1238.6,
eur: 1038.6,
color: '#94a3b8',
},
],
Expand Down
3 changes: 2 additions & 1 deletion docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const DOCS_DESCRIPTION =
'Contributor guide and architecture docs for Feel Your Protocol — interactive Ethereum protocol explorations, E-Components, and open-source development.'

/** Stable path under `docs/public/og/` — copied to `dist/docs/og/` on build. */
const DOCS_OG_IMAGE_PATH = '/og/default.png'
const DOCS_OG_IMAGE_PATH = '/og/default.webp'
const DOCS_OG_IMAGE = `${DOCS_ORIGIN}${DOCS_OG_IMAGE_PATH}`
const DOCS_OG_IMAGE_WIDTH = '1200'
const DOCS_OG_IMAGE_HEIGHT = '630'
Expand Down Expand Up @@ -46,6 +46,7 @@ export default defineConfig({
['meta', { property: 'og:image:width', content: DOCS_OG_IMAGE_WIDTH }],
['meta', { property: 'og:image:height', content: DOCS_OG_IMAGE_HEIGHT }],
['meta', { property: 'og:image:alt', content: DOCS_OG_IMAGE_ALT }],
['meta', { property: 'og:image:type', content: 'image/webp' }],
['meta', { name: 'twitter:image', content: DOCS_OG_IMAGE }],
['meta', { name: 'twitter:image:alt', content: DOCS_OG_IMAGE_ALT }],
],
Expand Down
3 changes: 2 additions & 1 deletion docs/contributing/adding-an-exploration.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ export const INFO: Exploration = {
| `topic` | Yes | Topic ID this exploration belongs to. Must be one of the fixed set: `scaling`, `privacy`, `ux`, `security`, `robustness`, `interoperability`. Topics are static and not added via contributions — see [Architecture](/guide/architecture#topics) for the full list. |
| `timeline` | Yes | Timeline ID for this exploration (e.g. `fusaka`, `glamsterdam`, `ready`, `research`, `ideas`). See [Architecture](/guide/architecture) for details. |
| `tags` | Yes | Array of `Tag` enum values (max 3–4). Tags are broader technical concepts that must be reusable across explorations. New tags can be proposed — see [Architecture](/guide/architecture#tags) for rules and the current list. |
| `image` | No | Imported image for topic overview display — see [Images](/contributing/images) for format, palette, and style guidance |
| `image` | No | Full cover image for exploration pages — see [Images](/contributing/images) |
| `imageSmall` | No | Optional thumbnail (`image_small.*`) for home/topic cards; falls back to `image` |
| `introText` | No | HTML-formatted introduction paragraph |
| `usageText` | No | HTML-formatted usage instructions |
| `creatorName` | No | Display name of the exploration's creator |
Expand Down
25 changes: 23 additions & 2 deletions docs/contributing/images.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,35 @@ The image file goes into your exploration folder as `image.webp` (or `.png`, `.j

```typescript
import image from './image.webp'
import imageSmall from './image_small.webp'

export const INFO: Exploration = {
// ...
image,
imageSmall, // optional — see Thumbnail below
// ...
}
```

### Thumbnail (`image_small`)

Exploration pages use the full cover image; **home page topic cards**, **topic overview pages**, and similar compact layouts use a smaller variant when available.

| File | Role |
|------|------|
| `image.webp` (or `.jpg`, …) | Full cover — exploration sidebar and detail pages |
| `image_small.webp` | Optional thumbnail — same format, `_small` suffix |

Naming: if the cover is `image.webp`, the thumbnail is `image_small.webp`; for `image.jpg` use `image_small.jpg`.

| Property | Recommendation |
|----------|---------------|
| **Width** | ~300px (matches max display ~144–224px at 2× retina) |
| **Aspect ratio** | Same as cover |
| **File size** | Under 40 KB |

If `image_small` is omitted, compact layouts fall back to the full cover image.

## Color Palette

This is the one area with strict rules. The color palette for your image must be derived from the **topic color** assigned to your exploration. This keeps the site visually coherent across contributions.
Expand Down Expand Up @@ -395,8 +416,8 @@ Colors — strict:
| Rule | Detail |
|------|--------|
| Format | WebP preferred; PNG, JPEG, SVG also accepted |
| Orientation | Portrait (3:4) |
| Resolution | 768×1024 recommended; min 512px short side, max 1536px long side |
| Cover | `image.webp` — 768×1024 recommended |
| Thumbnail | `image_small.webp` — optional ~300px for cards |
| File size | < 200 KB (WebP) or < 400 KB (PNG/JPEG) |
| Colors | Greyscale + topic color shades only; keep the two clearly separated |
| Composition | Abstract, one focal point, generous white margins at edges |
Expand Down
Binary file removed docs/public/og/default.png
Binary file not shown.
Binary file added docs/public/og/default.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed public/og/default.png
Binary file not shown.
Binary file added public/og/default.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 13 additions & 1 deletion src/explorations/REGISTRY.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ export interface Exploration {
timeline: string
tags: Tag[]
image?: string
/** Optional thumbnail (~300px) for topic cards and compact layouts; same basename as `image` with `_small` suffix. */
imageSmall?: string
/** Optional max height for the cover image in the exploration sidebar (CSS length, e.g. `12rem`). */
imageBoxHeight?: string
/** When set, exploration content may Teleport into `#exploration-right-panel`. */
Expand All @@ -55,14 +57,24 @@ export function pickRandom<T>(items: T[]): T | undefined {
return items[Math.floor(Math.random() * items.length)]
}

export function getExplorationCoverImage(exploration: Exploration): string | undefined {
return exploration.image
}

/** Thumbnail for home/topic cards and other compact layouts; falls back to cover image. */
export function getExplorationThumbnailImage(exploration: Exploration): string | undefined {
if (!exploration.image) return undefined
return exploration.imageSmall ?? exploration.image
}

export function getRandomExplorationWithImage(): Exploration | undefined {
return pickRandom(Object.values(EXPLORATIONS).filter((e) => e.image))
}

export function getRandomTopicExplorationImage(topicId: string): string | undefined {
const images = Object.values(EXPLORATIONS)
.filter((e) => e.topic === topicId && e.image)
.map((e) => e.image!)
.map((e) => getExplorationThumbnailImage(e)!)
return pickRandom(images)
}

Expand Down
35 changes: 35 additions & 0 deletions src/explorations/__tests__/registryImages.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { describe, expect, it } from 'vitest'

import {
EXPLORATIONS,
getExplorationCoverImage,
getExplorationThumbnailImage,
getRandomTopicExplorationImage,
} from '@/explorations/REGISTRY'

describe('exploration images', () => {
it('uses imageSmall for thumbnails when present', () => {
const exploration = EXPLORATIONS['eip-8024']!

expect(getExplorationCoverImage(exploration)).toBe(exploration.image)
expect(getExplorationThumbnailImage(exploration)).toBe(exploration.imageSmall)
expect(getExplorationThumbnailImage(exploration)).not.toBe(exploration.image)
})

it('falls back to cover image when imageSmall is missing', () => {
const exploration = { ...EXPLORATIONS['eip-8024']!, imageSmall: undefined }

expect(getExplorationThumbnailImage(exploration)).toBe(exploration.image)
})

it('returns thumbnail URLs for topic cards', () => {
const image = getRandomTopicExplorationImage('scaling')

expect(image).toBeDefined()
expect(
Object.values(EXPLORATIONS)
.filter((e) => e.topic === 'scaling')
.some((e) => getExplorationThumbnailImage(e) === image),
).toBe(true)
})
})
Binary file added src/explorations/eip-7594/image_small.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions src/explorations/eip-7594/info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { Exploration } from '@/explorations/REGISTRY'
import { Tag } from '@/explorations/TAGS'

import image from './image.webp'
import imageSmall from './image_small.webp'

export const INFO: Exploration = {
id: 'eip-7594',
Expand All @@ -14,6 +15,7 @@ export const INFO: Exploration = {
timeline: 'fusaka',
tags: [Tag.PeerDAS],
image,
imageSmall,
introText:
'<b>How do blob transactions change with PeerDAS?</b> ' +
'With the Fusaka hardfork, data availability sampling (DAS) replaces single blob proofs with ' +
Expand Down
Binary file added src/explorations/eip-7883/image_small.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions src/explorations/eip-7883/info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { Exploration } from '@/explorations/REGISTRY'
import { Tag } from '@/explorations/TAGS'

import image from './image.webp'
import imageSmall from './image_small.webp'

export const INFO: Exploration = {
id: 'eip-7883',
Expand All @@ -14,6 +15,7 @@ export const INFO: Exploration = {
timeline: 'fusaka',
tags: [Tag.GasCosts, Tag.Precompiles],
image,
imageSmall,
introText:
'<b>How are ModExp gas costs changing with Fusaka?</b> ' +
'EIP-7883 replaces the ModExp precompile gas formula with one that better reflects real ' +
Expand Down
Binary file added src/explorations/eip-7928/image_small.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions src/explorations/eip-7928/info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { Exploration } from '@/explorations/REGISTRY'
import { Tag } from '@/explorations/TAGS'

import image from './image.webp'
import imageSmall from './image_small.webp'

export const INFO: Exploration = {
id: 'eip-7928',
Expand All @@ -14,6 +15,7 @@ export const INFO: Exploration = {
timeline: 'glamsterdam',
tags: [Tag.BAL, Tag.EVM],
image,
imageSmall,
imageBoxHeight: '19rem',
rightPanel: true,
introText:
Expand Down
Binary file added src/explorations/eip-7951/image_small.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions src/explorations/eip-7951/info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { Exploration } from '@/explorations/REGISTRY'
import { Tag } from '@/explorations/TAGS'

import image from './image.webp'
import imageSmall from './image_small.webp'

export const INFO: Exploration = {
id: 'eip-7951',
Expand All @@ -14,6 +15,7 @@ export const INFO: Exploration = {
timeline: 'fusaka',
tags: [Tag.Precompiles, Tag.Signatures],
image,
imageSmall,
introText:
'<b>Why add a secp256r1 precompile?</b> ' +
'The curve (also known as P-256) is the native signing algorithm on ' +
Expand Down
Binary file added src/explorations/eip-8024/image_small.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions src/explorations/eip-8024/info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { Exploration } from '@/explorations/REGISTRY'
import { Tag } from '@/explorations/TAGS'

import image from './image.jpg'
import imageSmall from './image_small.jpg'

export const INFO: Exploration = {
id: 'eip-8024',
Expand All @@ -14,6 +15,7 @@ export const INFO: Exploration = {
timeline: 'glamsterdam',
tags: [Tag.EVM],
image,
imageSmall,
imageBoxHeight: '19rem',
rightPanel: true,
introText:
Expand Down
1 change: 1 addition & 0 deletions src/libs/__tests__/pageSeo.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ describe('pageSeo', () => {
)
expect(html).toContain('<meta property="og:image:width" content="1200">')
expect(html).toContain('<meta property="og:image:height" content="630">')
expect(html).toContain('<meta property="og:image:type" content="image/webp">')
expect(html).toContain('<meta name="twitter:card" content="summary_large_image">')
expect(html).toContain('application/ld+json')
})
Expand Down
4 changes: 4 additions & 0 deletions src/libs/applyPageSeo.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { PageSeo } from './seoCore'
import { inferOgImageType } from './seoCore'

const JSON_LD_ID = 'page-seo-jsonld'

Expand Down Expand Up @@ -78,6 +79,9 @@ export function applyPageSeo(seo: PageSeo): void {
setProperty('og:image:height', String(seo.imageHeight))
setProperty('og:image:alt', seo.imageAlt)

const imageType = inferOgImageType(seo.imageUrl)
setProperty('og:image:type', imageType)

setMeta('twitter:card', 'summary_large_image')
setMeta('twitter:title', seo.title)
setMeta('twitter:description', seo.description)
Expand Down
2 changes: 1 addition & 1 deletion src/libs/pageSeo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const DEFAULT_DESCRIPTION =
'Interactive open-source explorations of Ethereum protocol changes. Real EVM and crypto libraries running in your browser.'

/** Stable path under `public/og/` — copied verbatim to `dist/website/og/` on build. */
export const DEFAULT_OG_IMAGE_PATH = '/og/default.png'
export const DEFAULT_OG_IMAGE_PATH = '/og/default.webp'
export const DEFAULT_OG_IMAGE_WIDTH = 1200
export const DEFAULT_OG_IMAGE_HEIGHT = 630

Expand Down
18 changes: 17 additions & 1 deletion src/libs/seoCore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ export function escapeHtml(text: string): string {
.replace(/"/g, '&quot;')
}

export function inferOgImageType(imageUrl: string): string | undefined {
const path = imageUrl.split('?')[0]?.toLowerCase() ?? ''
if (path.endsWith('.webp')) return 'image/webp'
if (path.endsWith('.png')) return 'image/png'
if (path.endsWith('.jpg') || path.endsWith('.jpeg')) return 'image/jpeg'
return undefined
}

/** Inject title, meta, canonical, Open Graph, and JSON-LD into a Vite-built index.html shell. */
export function injectSeoIntoHtml(
html: string,
Expand All @@ -56,12 +64,20 @@ export function injectSeoIntoHtml(
`<meta property="og:image:width" content="${seo.imageWidth}">`,
`<meta property="og:image:height" content="${seo.imageHeight}">`,
`<meta property="og:image:alt" content="${escapeHtml(seo.imageAlt)}">`,
]

const imageType = inferOgImageType(seo.imageUrl)
if (imageType) {
headTags.push(`<meta property="og:image:type" content="${imageType}">`)
}

headTags.push(
`<meta name="twitter:card" content="summary_large_image">`,
`<meta name="twitter:title" content="${escapeHtml(seo.title)}">`,
`<meta name="twitter:description" content="${escapeHtml(seo.description)}">`,
`<meta name="twitter:image" content="${escapeHtml(seo.imageUrl)}">`,
`<meta name="twitter:image:alt" content="${escapeHtml(seo.imageAlt)}">`,
]
)

if (seo.noindex) {
headTags.push('<meta name="robots" content="noindex, follow">')
Expand Down
7 changes: 5 additions & 2 deletions src/views/NotFoundView.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
<script setup lang="ts">
import { getRandomExplorationWithImage } from '@/explorations/REGISTRY'
import {
getExplorationThumbnailImage,
getRandomExplorationWithImage,
} from '@/explorations/REGISTRY'
import { TOPIC_COLORS, topicCSSVars, TOPICS } from '@/explorations/TOPICS'
import { DOCS_ADD_EXPLORATION } from '@/libs/docsUrls'

Expand Down Expand Up @@ -69,7 +72,7 @@ function getImageUrl(image: string): string {
class="block shrink-0 mt-6 md:mt-0 no-underline group"
>
<img
:src="getImageUrl(pick.image)"
:src="getImageUrl(getExplorationThumbnailImage(pick)!)"
:alt="pick.title"
class="rounded-md max-w-[14rem] mx-auto md:mx-0 transition group-hover:opacity-90"
/>
Expand Down
5 changes: 3 additions & 2 deletions treasury/2026/06/ethereumjs.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
---
month: 2026-06
hourly_rate_eur: 50
total_hours: 0
total_eur: 0
total_hours: 2
total_eur: 100
---

# EthereumJS work — June 2026

| Date | CW | Hours | Task |
| ---- | -- | ----- | ---- |
| 2026-06-11 | 24 | 2 | Logs + EIP-7708 preparation |
Loading