diff --git a/public/icons/cat-communication.svg b/public/icons/cat-communication.svg new file mode 100644 index 0000000..e4cbe2b --- /dev/null +++ b/public/icons/cat-communication.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/public/icons/cat-devtools.svg b/public/icons/cat-devtools.svg new file mode 100644 index 0000000..fa66a19 --- /dev/null +++ b/public/icons/cat-devtools.svg @@ -0,0 +1,8 @@ + + + + + + + diff --git a/public/icons/cat-games.svg b/public/icons/cat-games.svg new file mode 100644 index 0000000..a06eaaf --- /dev/null +++ b/public/icons/cat-games.svg @@ -0,0 +1,9 @@ + + + + + + + + diff --git a/public/icons/cat-media.svg b/public/icons/cat-media.svg new file mode 100644 index 0000000..d933510 --- /dev/null +++ b/public/icons/cat-media.svg @@ -0,0 +1,7 @@ + + + + + + diff --git a/public/icons/cat-other.svg b/public/icons/cat-other.svg new file mode 100644 index 0000000..fffaf5c --- /dev/null +++ b/public/icons/cat-other.svg @@ -0,0 +1,8 @@ + + + + + + + diff --git a/public/icons/cat-privacy.svg b/public/icons/cat-privacy.svg new file mode 100644 index 0000000..7ed4970 --- /dev/null +++ b/public/icons/cat-privacy.svg @@ -0,0 +1,7 @@ + + + + + + diff --git a/public/icons/cat-utilities.svg b/public/icons/cat-utilities.svg new file mode 100644 index 0000000..b79d22d --- /dev/null +++ b/public/icons/cat-utilities.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/public/icons/cat-wallets.svg b/public/icons/cat-wallets.svg new file mode 100644 index 0000000..35f95ee --- /dev/null +++ b/public/icons/cat-wallets.svg @@ -0,0 +1,9 @@ + + + + + + + + B + diff --git a/public/style.css b/public/style.css index 9e53bb5..14e1dc4 100644 --- a/public/style.css +++ b/public/style.css @@ -168,7 +168,9 @@ footer .fine{max-width:660px;margin:.8rem auto 0;line-height:1.7} display:flex;flex-direction:column;gap:.6rem; } .cat:hover{transform:translateY(-3px);border-color:rgba(214,255,61,.35);background:var(--panel2)} -.cat .ico{font-size:26px;line-height:1;filter:grayscale(.2)} +.cat .ico{width:40px;height:40px;display:block;transition:transform .18s ease} +.cat:hover .ico{transform:rotate(-3deg)} +.cat.empty .ico{opacity:.75} .cat h3{margin:.1rem 0 0;font-size:1.02rem} .cat .count{color:var(--dim);font-size:12px;letter-spacing:.02em;margin-top:auto;padding-top:.4rem} .cat.empty .count::before{content:"— ";color:var(--dim)} diff --git a/service/pages-browse.js b/service/pages-browse.js index 1e06f38..98ed9a8 100644 --- a/service/pages-browse.js +++ b/service/pages-browse.js @@ -30,22 +30,26 @@ import { softwareApplicationLd } from './seo.js'; * allow-list in listings.js. The browse grid picks up the new row * automatically. */ +// `icon` is a path to an SVG under /static/icons/. Rendered via at the +// three sizes the templates use (26 px in the grid, 40 px in the category +// hero, 1em in the inline chip on listing detail pages). The icons share a +// family with the sibling .x marks: 12-radius dark square + acid-green mark. export const CATEGORIES = [ - { slug: 'communication', icon: '💬', title: 'Communication', + { slug: 'communication', icon: '/static/icons/cat-communication.svg', title: 'Communication', blurb: `Messengers, voice, boards, group chats. Clients that route through peers rather than someone else's server.` }, - { slug: 'privacy', icon: '🛡️', title: 'Privacy & security', + { slug: 'privacy', icon: '/static/icons/cat-privacy.svg', title: 'Privacy & security', blurb: `Tor and mix-net tooling, key and vault managers, disk crypto. The things a conventional store delists first when a government writes.` }, - { slug: 'wallets', icon: '💸', title: 'Wallets & payments', + { slug: 'wallets', icon: '/static/icons/cat-wallets.svg', title: 'Wallets & payments', blurb: `BCH wallets, cross-chain bridges, point-of-sale terminals. The chain publishes the listing and the chain settles the payment.` }, - { slug: 'games', icon: '🎮', title: 'Games', + { slug: 'games', icon: '/static/icons/cat-games.svg', title: 'Games', blurb: `Standalone titles, on-chain leaderboards, in-app purchases that reach the publisher instead of a 30% toll.` }, - { slug: 'devtools', icon: '🧰', title: 'Developer tools', + { slug: 'devtools', icon: '/static/icons/cat-devtools.svg', title: 'Developer tools', blurb: `SDKs, libraries, indexers, faucets, protocol utilities. The pieces other apps on this list are built out of.` }, - { slug: 'media', icon: '🎧', title: 'Media & publishing', + { slug: 'media', icon: '/static/icons/cat-media.svg', title: 'Media & publishing', blurb: `Readers, players, one-file blogs, podcast hosts. Clients that find their audience without going through an aggregator.` }, - { slug: 'utilities', icon: '⚙️', title: 'Utilities', + { slug: 'utilities', icon: '/static/icons/cat-utilities.svg', title: 'Utilities', blurb: `File tools, format converters, backup, calculators. The small sharp things that solve one problem and do not ask for an account first.` }, - { slug: 'other', icon: '✨', title: 'Everything else', + { slug: 'other', icon: '/static/icons/cat-other.svg', title: 'Everything else', blurb: `Anything that does not fit above. New categories are proposed in-band — any listing can declare one and clients will offer to add it.` }, ]; @@ -77,7 +81,7 @@ export function renderCategoriesGrid({ verbose = true } = {}) { const emptyCls = n === 0 ? ' empty' : ''; const countLabel = `${n} ${n === 1 ? 'listing' : 'listings'}`; return ` -
${c.icon}
+

${escapeHtml(c.title)}

${verbose ? `

${escapeHtml(c.blurb)}

` : ''} ${countLabel} @@ -102,7 +106,7 @@ export function categoryPage(req, slug) { here, body: `
-
${cat.icon}
+

${escapeHtml(title)}

${escapeHtml(cat.blurb)}

@@ -136,7 +140,7 @@ export function appPage(req, publisher, slug) { const cat = CATEGORY_BY_SLUG[row.category]; const catChip = cat - ? `
${cat.icon} ${escapeHtml(cat.title)}` + ? ` ${escapeHtml(cat.title)}` : ''; const scanReport = safeParse(row.scan_report); @@ -150,7 +154,10 @@ export function appPage(req, publisher, slug) { const iconHtml = row.icon ? `` - : `
${cat?.icon || '📦'}
`; + : cat?.icon + ? `` + : `
📦
`; return { title: `${row.name} by ${row.publisher}`,