docs(website): add User Stories and Use Cases collage page (#18282)
Adds a new top-of-sidebar docs page at /docs/user-stories that is a masonry-style collage of 99 real user stories sourced from X/Twitter, GitHub issues/PRs, Reddit, Hacker News, YouTube, blogs (Medium, Substack, dev.to), podcasts, LinkedIn, GitHub Gists, and Product Hunt. Every tile links to the original post/issue/video/gist where someone described a specific use case: personal assistants, dev workflows, trading bots, research briefs, family WhatsApp agents, Kubernetes deployments, legal-domain self-hosted setups, and more. - docs/user-stories.mdx: MDX entry mounting the collage component - src/components/UserStoriesCollage: React component with category + source filters, CSS-columns masonry layout, per-category accent colors - src/data/userStories.json: source-of-truth dataset (force-added; the root .gitignore's unanchored 'data/' rule would otherwise swallow it, same reason skills.json is explicitly listed in website/.gitignore) - sidebars.ts: link added at the top of the docs sidebar
This commit is contained in:
parent
dfe512c58d
commit
a2a32688ca
10
website/docs/user-stories.mdx
Normal file
10
website/docs/user-stories.mdx
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
title: User Stories & Use Cases
|
||||||
|
description: Real stories from the Hermes Agent community — what people are actually building, scraped from X, GitHub, Reddit, Hacker News, YouTube, blogs, and podcasts.
|
||||||
|
hide_title: true
|
||||||
|
hide_table_of_contents: true
|
||||||
|
---
|
||||||
|
|
||||||
|
import UserStoriesCollage from '@site/src/components/UserStoriesCollage';
|
||||||
|
|
||||||
|
<UserStoriesCollage />
|
||||||
@ -2,6 +2,7 @@ import type {SidebarsConfig} from '@docusaurus/plugin-content-docs';
|
|||||||
|
|
||||||
const sidebars: SidebarsConfig = {
|
const sidebars: SidebarsConfig = {
|
||||||
docs: [
|
docs: [
|
||||||
|
'user-stories',
|
||||||
{
|
{
|
||||||
type: 'category',
|
type: 'category',
|
||||||
label: 'Getting Started',
|
label: 'Getting Started',
|
||||||
|
|||||||
310
website/src/components/UserStoriesCollage/index.tsx
Normal file
310
website/src/components/UserStoriesCollage/index.tsx
Normal file
@ -0,0 +1,310 @@
|
|||||||
|
import React, { useMemo, useState } from 'react';
|
||||||
|
import stories from '@site/src/data/userStories.json';
|
||||||
|
import styles from './styles.module.css';
|
||||||
|
|
||||||
|
interface Story {
|
||||||
|
id: string;
|
||||||
|
source: string;
|
||||||
|
author: string;
|
||||||
|
url: string;
|
||||||
|
date: string;
|
||||||
|
category: string;
|
||||||
|
headline: string;
|
||||||
|
quote: string;
|
||||||
|
size: 'sm' | 'md' | 'lg';
|
||||||
|
}
|
||||||
|
|
||||||
|
const allStories = stories as Story[];
|
||||||
|
|
||||||
|
// Category → pretty label + accent colors (solid + soft fill + gradient top-strip)
|
||||||
|
const CATEGORIES: Record<
|
||||||
|
string,
|
||||||
|
{ label: string; solid: string; soft: string; strip: string }
|
||||||
|
> = {
|
||||||
|
'dev-workflow': {
|
||||||
|
label: 'Dev Workflow',
|
||||||
|
solid: '#60a5fa',
|
||||||
|
soft: 'rgba(96, 165, 250, 0.14)',
|
||||||
|
strip: 'linear-gradient(90deg, #3b82f6, #60a5fa, #a78bfa)',
|
||||||
|
},
|
||||||
|
'personal-assistant': {
|
||||||
|
label: 'Personal Assistant',
|
||||||
|
solid: '#34d399',
|
||||||
|
soft: 'rgba(52, 211, 153, 0.14)',
|
||||||
|
strip: 'linear-gradient(90deg, #10b981, #34d399, #a7f3d0)',
|
||||||
|
},
|
||||||
|
'content-creation': {
|
||||||
|
label: 'Content Creation',
|
||||||
|
solid: '#f472b6',
|
||||||
|
soft: 'rgba(244, 114, 182, 0.14)',
|
||||||
|
strip: 'linear-gradient(90deg, #ec4899, #f472b6, #fda4af)',
|
||||||
|
},
|
||||||
|
'business-ops': {
|
||||||
|
label: 'Business Ops',
|
||||||
|
solid: '#fb923c',
|
||||||
|
soft: 'rgba(251, 146, 60, 0.14)',
|
||||||
|
strip: 'linear-gradient(90deg, #f97316, #fb923c, #fcd34d)',
|
||||||
|
},
|
||||||
|
trading: {
|
||||||
|
label: 'Trading & Markets',
|
||||||
|
solid: '#facc15',
|
||||||
|
soft: 'rgba(250, 204, 21, 0.16)',
|
||||||
|
strip: 'linear-gradient(90deg, #eab308, #facc15, #fde047)',
|
||||||
|
},
|
||||||
|
research: {
|
||||||
|
label: 'Research',
|
||||||
|
solid: '#a78bfa',
|
||||||
|
soft: 'rgba(167, 139, 250, 0.14)',
|
||||||
|
strip: 'linear-gradient(90deg, #8b5cf6, #a78bfa, #c4b5fd)',
|
||||||
|
},
|
||||||
|
creative: {
|
||||||
|
label: 'Creative',
|
||||||
|
solid: '#f87171',
|
||||||
|
soft: 'rgba(248, 113, 113, 0.14)',
|
||||||
|
strip: 'linear-gradient(90deg, #ef4444, #f87171, #fca5a5)',
|
||||||
|
},
|
||||||
|
marketing: {
|
||||||
|
label: 'Marketing',
|
||||||
|
solid: '#e879f9',
|
||||||
|
soft: 'rgba(232, 121, 249, 0.14)',
|
||||||
|
strip: 'linear-gradient(90deg, #d946ef, #e879f9, #f0abfc)',
|
||||||
|
},
|
||||||
|
integrations: {
|
||||||
|
label: 'Integrations',
|
||||||
|
solid: '#38bdf8',
|
||||||
|
soft: 'rgba(56, 189, 248, 0.14)',
|
||||||
|
strip: 'linear-gradient(90deg, #0ea5e9, #38bdf8, #7dd3fc)',
|
||||||
|
},
|
||||||
|
enterprise: {
|
||||||
|
label: 'Enterprise',
|
||||||
|
solid: '#94a3b8',
|
||||||
|
soft: 'rgba(148, 163, 184, 0.16)',
|
||||||
|
strip: 'linear-gradient(90deg, #64748b, #94a3b8, #cbd5e1)',
|
||||||
|
},
|
||||||
|
messaging: {
|
||||||
|
label: 'Messaging',
|
||||||
|
solid: '#22d3ee',
|
||||||
|
soft: 'rgba(34, 211, 238, 0.14)',
|
||||||
|
strip: 'linear-gradient(90deg, #06b6d4, #22d3ee, #67e8f9)',
|
||||||
|
},
|
||||||
|
privacy: {
|
||||||
|
label: 'Privacy & Self-Hosted',
|
||||||
|
solid: '#4ade80',
|
||||||
|
soft: 'rgba(74, 222, 128, 0.14)',
|
||||||
|
strip: 'linear-gradient(90deg, #16a34a, #4ade80, #86efac)',
|
||||||
|
},
|
||||||
|
'cost-optimization': {
|
||||||
|
label: 'Cost Optimization',
|
||||||
|
solid: '#fbbf24',
|
||||||
|
soft: 'rgba(251, 191, 36, 0.16)',
|
||||||
|
strip: 'linear-gradient(90deg, #f59e0b, #fbbf24, #fde68a)',
|
||||||
|
},
|
||||||
|
meta: {
|
||||||
|
label: 'Meta & Ecosystem',
|
||||||
|
solid: '#c084fc',
|
||||||
|
soft: 'rgba(192, 132, 252, 0.14)',
|
||||||
|
strip: 'linear-gradient(90deg, #a855f7, #c084fc, #d8b4fe)',
|
||||||
|
},
|
||||||
|
general: {
|
||||||
|
label: 'General',
|
||||||
|
solid: '#9ca3af',
|
||||||
|
soft: 'rgba(156, 163, 175, 0.16)',
|
||||||
|
strip: 'linear-gradient(90deg, #6b7280, #9ca3af, #d1d5db)',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Source → compact label shown in the badge row
|
||||||
|
const SOURCE_LABELS: Record<string, string> = {
|
||||||
|
x: 'X · Twitter',
|
||||||
|
hn: 'Hacker News',
|
||||||
|
reddit: 'Reddit',
|
||||||
|
github: 'GitHub',
|
||||||
|
youtube: 'YouTube',
|
||||||
|
blog: 'Blog',
|
||||||
|
podcast: 'Podcast',
|
||||||
|
linkedin: 'LinkedIn',
|
||||||
|
gist: 'GitHub Gist',
|
||||||
|
producthunt: 'Product Hunt',
|
||||||
|
};
|
||||||
|
|
||||||
|
function sourceColor(source: string): string {
|
||||||
|
switch (source) {
|
||||||
|
case 'x': return '#1d9bf0';
|
||||||
|
case 'hn': return '#ff6600';
|
||||||
|
case 'reddit': return '#ff4500';
|
||||||
|
case 'github': return '#8b949e';
|
||||||
|
case 'youtube': return '#ff0033';
|
||||||
|
case 'blog': return '#a78bfa';
|
||||||
|
case 'podcast': return '#8b5cf6';
|
||||||
|
case 'linkedin': return '#0a66c2';
|
||||||
|
case 'gist': return '#8b949e';
|
||||||
|
case 'producthunt': return '#da552f';
|
||||||
|
default: return '#64748b';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function UserStoriesCollage(): JSX.Element {
|
||||||
|
const [activeCategory, setActiveCategory] = useState<string>('all');
|
||||||
|
const [activeSource, setActiveSource] = useState<string>('all');
|
||||||
|
|
||||||
|
const categoryCounts = useMemo(() => {
|
||||||
|
const counts: Record<string, number> = {};
|
||||||
|
for (const s of allStories) counts[s.category] = (counts[s.category] ?? 0) + 1;
|
||||||
|
return counts;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const sourceCounts = useMemo(() => {
|
||||||
|
const counts: Record<string, number> = {};
|
||||||
|
for (const s of allStories) counts[s.source] = (counts[s.source] ?? 0) + 1;
|
||||||
|
return counts;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const visible = useMemo(() => {
|
||||||
|
return allStories.filter((s) => {
|
||||||
|
if (activeCategory !== 'all' && s.category !== activeCategory) return false;
|
||||||
|
if (activeSource !== 'all' && s.source !== activeSource) return false;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}, [activeCategory, activeSource]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.wrap}>
|
||||||
|
<div className={styles.hero}>
|
||||||
|
<h1>User Stories & Use Cases</h1>
|
||||||
|
<p>
|
||||||
|
What the Hermes Agent community is actually building. Every tile
|
||||||
|
below links to a real post, issue, video, or gist where someone
|
||||||
|
describes how they use Hermes — scraped from X, GitHub, Reddit,
|
||||||
|
Hacker News, YouTube, blogs, and podcasts.
|
||||||
|
</p>
|
||||||
|
<div className={styles.meta}>
|
||||||
|
<span><strong>{allStories.length}</strong> stories</span>
|
||||||
|
<span><strong>{Object.keys(categoryCounts).length}</strong> categories</span>
|
||||||
|
<span><strong>{Object.keys(sourceCounts).length}</strong> sources</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Category filters */}
|
||||||
|
<div className={styles.filters}>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={`${styles.filterBtn} ${activeCategory === 'all' ? styles.filterActive : ''}`}
|
||||||
|
onClick={() => setActiveCategory('all')}
|
||||||
|
>
|
||||||
|
All<span className={styles.filterCount}>{allStories.length}</span>
|
||||||
|
</button>
|
||||||
|
{Object.entries(CATEGORIES)
|
||||||
|
.filter(([key]) => categoryCounts[key])
|
||||||
|
.sort((a, b) => (categoryCounts[b[0]] ?? 0) - (categoryCounts[a[0]] ?? 0))
|
||||||
|
.map(([key, meta]) => (
|
||||||
|
<button
|
||||||
|
key={key}
|
||||||
|
type="button"
|
||||||
|
className={`${styles.filterBtn} ${activeCategory === key ? styles.filterActive : ''}`}
|
||||||
|
onClick={() => setActiveCategory(key)}
|
||||||
|
style={
|
||||||
|
activeCategory === key
|
||||||
|
? { background: meta.solid, borderColor: meta.solid, color: '#0f172a' }
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{meta.label}
|
||||||
|
<span className={styles.filterCount}>{categoryCounts[key]}</span>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Source filters — smaller, secondary row */}
|
||||||
|
<div className={styles.filters} style={{ marginTop: '-0.75rem' }}>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={`${styles.filterBtn} ${activeSource === 'all' ? styles.filterActive : ''}`}
|
||||||
|
onClick={() => setActiveSource('all')}
|
||||||
|
style={{ fontSize: '0.72rem' }}
|
||||||
|
>
|
||||||
|
All sources
|
||||||
|
</button>
|
||||||
|
{Object.entries(SOURCE_LABELS)
|
||||||
|
.filter(([key]) => sourceCounts[key])
|
||||||
|
.map(([key, label]) => (
|
||||||
|
<button
|
||||||
|
key={key}
|
||||||
|
type="button"
|
||||||
|
className={`${styles.filterBtn} ${activeSource === key ? styles.filterActive : ''}`}
|
||||||
|
onClick={() => setActiveSource(key)}
|
||||||
|
style={{
|
||||||
|
fontSize: '0.72rem',
|
||||||
|
...(activeSource === key
|
||||||
|
? { background: sourceColor(key), borderColor: sourceColor(key), color: '#fff' }
|
||||||
|
: {}),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{label}
|
||||||
|
<span className={styles.filterCount}>{sourceCounts[key]}</span>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Collage grid */}
|
||||||
|
{visible.length === 0 ? (
|
||||||
|
<div className={styles.empty}>No stories match that filter.</div>
|
||||||
|
) : (
|
||||||
|
<div className={styles.grid}>
|
||||||
|
{visible.map((s) => {
|
||||||
|
const cat = CATEGORIES[s.category] ?? CATEGORIES.general;
|
||||||
|
const sizeClass =
|
||||||
|
s.size === 'lg' ? styles.tileLg : s.size === 'sm' ? styles.tileSm : styles.tileMd;
|
||||||
|
const srcColor = sourceColor(s.source);
|
||||||
|
return (
|
||||||
|
<a
|
||||||
|
key={s.id}
|
||||||
|
className={`${styles.tile} ${sizeClass}`}
|
||||||
|
href={s.url}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
'--tile-accent': cat.strip,
|
||||||
|
'--tile-accent-solid': cat.solid,
|
||||||
|
'--tile-accent-soft': cat.soft,
|
||||||
|
} as React.CSSProperties
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className={styles.badgeRow}>
|
||||||
|
<span className={styles.sourceBadge}>
|
||||||
|
<span className={styles.sourceIcon} style={{ background: srcColor }} />
|
||||||
|
{SOURCE_LABELS[s.source] ?? s.source}
|
||||||
|
</span>
|
||||||
|
<span className={styles.catTag}>{cat.label}</span>
|
||||||
|
</div>
|
||||||
|
<h3 className={styles.headline}>{s.headline}</h3>
|
||||||
|
<p className={styles.quote}>“{s.quote}”</p>
|
||||||
|
<span className={styles.author}>
|
||||||
|
{s.author}
|
||||||
|
{s.date ? <> · {s.date}</> : null}
|
||||||
|
</span>
|
||||||
|
<span className={styles.external} aria-hidden="true">↗</span>
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className={styles.footer}>
|
||||||
|
Built something with Hermes?{' '}
|
||||||
|
<a
|
||||||
|
href="https://github.com/NousResearch/hermes-agent/edit/main/website/src/data/userStories.json"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
Add your story to this page
|
||||||
|
</a>{' '}
|
||||||
|
by editing <code>userStories.json</code>, or post it in the{' '}
|
||||||
|
<a href="https://discord.gg/NousResearch" target="_blank" rel="noopener noreferrer">
|
||||||
|
Nous Research Discord
|
||||||
|
</a>{' '}
|
||||||
|
and we'll pick it up.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
252
website/src/components/UserStoriesCollage/styles.module.css
Normal file
252
website/src/components/UserStoriesCollage/styles.module.css
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
/* User Stories collage — masonry grid with category-driven accents. */
|
||||||
|
|
||||||
|
.wrap {
|
||||||
|
max-width: 1280px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0 0 4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero {
|
||||||
|
padding: 2.5rem 0 2rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.hero h1 {
|
||||||
|
font-size: clamp(2rem, 4vw, 3.25rem);
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
background: linear-gradient(120deg, #a78bfa 0%, #60a5fa 50%, #34d399 100%);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
}
|
||||||
|
.hero p {
|
||||||
|
max-width: 680px;
|
||||||
|
margin: 0 auto;
|
||||||
|
color: var(--ifm-color-emphasis-700);
|
||||||
|
font-size: 1.05rem;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meta {
|
||||||
|
display: flex;
|
||||||
|
gap: 1.5rem;
|
||||||
|
justify-content: center;
|
||||||
|
margin-top: 1.25rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: var(--ifm-color-emphasis-600);
|
||||||
|
}
|
||||||
|
.meta strong {
|
||||||
|
color: var(--ifm-color-emphasis-900);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Filter bar */
|
||||||
|
.filters {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.4rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 1.75rem 0 2rem;
|
||||||
|
padding: 0 1rem;
|
||||||
|
}
|
||||||
|
.filterBtn {
|
||||||
|
padding: 0.35rem 0.85rem;
|
||||||
|
border-radius: 999px;
|
||||||
|
border: 1px solid var(--ifm-color-emphasis-300);
|
||||||
|
background: transparent;
|
||||||
|
color: var(--ifm-color-emphasis-800);
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.18s ease;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.filterBtn:hover {
|
||||||
|
border-color: var(--ifm-color-emphasis-500);
|
||||||
|
color: var(--ifm-color-emphasis-1000);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
.filterActive {
|
||||||
|
background: var(--ifm-color-emphasis-900);
|
||||||
|
color: var(--ifm-background-color);
|
||||||
|
border-color: var(--ifm-color-emphasis-900);
|
||||||
|
}
|
||||||
|
[data-theme='dark'] .filterActive {
|
||||||
|
background: #e2e8f0;
|
||||||
|
color: #0f172a;
|
||||||
|
border-color: #e2e8f0;
|
||||||
|
}
|
||||||
|
.filterCount {
|
||||||
|
margin-left: 0.35rem;
|
||||||
|
opacity: 0.5;
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Masonry — use CSS columns for a true collage feel */
|
||||||
|
.grid {
|
||||||
|
column-count: 4;
|
||||||
|
column-gap: 1rem;
|
||||||
|
padding: 0 1rem;
|
||||||
|
}
|
||||||
|
@media (max-width: 1200px) { .grid { column-count: 3; } }
|
||||||
|
@media (max-width: 850px) { .grid { column-count: 2; } }
|
||||||
|
@media (max-width: 560px) { .grid { column-count: 1; } }
|
||||||
|
|
||||||
|
/* Tile */
|
||||||
|
.tile {
|
||||||
|
break-inside: avoid;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
padding: 1.1rem 1.2rem 1.15rem;
|
||||||
|
border-radius: 14px;
|
||||||
|
border: 1px solid var(--ifm-color-emphasis-200);
|
||||||
|
background: var(--ifm-card-background-color, var(--ifm-background-surface-color));
|
||||||
|
color: inherit !important;
|
||||||
|
text-decoration: none !important;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: transform 0.22s ease, box-shadow 0.22s ease, border-color 0.22s ease;
|
||||||
|
}
|
||||||
|
.tile::before {
|
||||||
|
/* Color accent strip */
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0; left: 0; right: 0;
|
||||||
|
height: 3px;
|
||||||
|
background: var(--tile-accent, linear-gradient(90deg, #a78bfa, #60a5fa));
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
.tile::after {
|
||||||
|
/* Subtle hover glow */
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
inset: -1px;
|
||||||
|
border-radius: 14px;
|
||||||
|
box-shadow: 0 0 0 0 transparent;
|
||||||
|
pointer-events: none;
|
||||||
|
transition: box-shadow 0.22s ease;
|
||||||
|
}
|
||||||
|
.tile:hover {
|
||||||
|
transform: translateY(-3px);
|
||||||
|
border-color: var(--tile-accent-solid, var(--ifm-color-primary));
|
||||||
|
box-shadow: 0 8px 24px -8px rgba(0, 0, 0, 0.25);
|
||||||
|
}
|
||||||
|
[data-theme='dark'] .tile:hover {
|
||||||
|
box-shadow: 0 10px 30px -12px rgba(120, 120, 200, 0.45);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Size variants — big tiles get more visual weight */
|
||||||
|
.tileSm { min-height: 130px; }
|
||||||
|
.tileMd { min-height: 180px; }
|
||||||
|
.tileLg {
|
||||||
|
min-height: 240px;
|
||||||
|
padding: 1.35rem 1.45rem 1.45rem;
|
||||||
|
}
|
||||||
|
.tileLg .headline {
|
||||||
|
font-size: 1.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tile body */
|
||||||
|
.badgeRow {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
font-size: 0.7rem;
|
||||||
|
letter-spacing: 0.06em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--ifm-color-emphasis-600);
|
||||||
|
}
|
||||||
|
.sourceBadge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.35rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.sourceIcon {
|
||||||
|
display: inline-block;
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
border-radius: 3px;
|
||||||
|
background: var(--tile-accent-solid, #a78bfa);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.catTag {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0.15rem 0.55rem;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: var(--tile-accent-soft, rgba(167, 139, 250, 0.12));
|
||||||
|
color: var(--tile-accent-solid, #a78bfa);
|
||||||
|
font-weight: 600;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.headline {
|
||||||
|
font-size: 1.02rem;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1.3;
|
||||||
|
margin: 0 0 0.5rem;
|
||||||
|
color: var(--ifm-color-emphasis-1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
.quote {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
line-height: 1.55;
|
||||||
|
color: var(--ifm-color-emphasis-800);
|
||||||
|
margin: 0;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 6;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.tileLg .quote { -webkit-line-clamp: 8; }
|
||||||
|
.tileSm .quote { -webkit-line-clamp: 4; }
|
||||||
|
|
||||||
|
.author {
|
||||||
|
display: block;
|
||||||
|
margin-top: 0.7rem;
|
||||||
|
font-size: 0.78rem;
|
||||||
|
color: var(--ifm-color-emphasis-600);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.external {
|
||||||
|
position: absolute;
|
||||||
|
top: 0.9rem;
|
||||||
|
right: 0.9rem;
|
||||||
|
opacity: 0;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: var(--tile-accent-solid, var(--ifm-color-primary));
|
||||||
|
transition: opacity 0.2s ease, transform 0.2s ease;
|
||||||
|
}
|
||||||
|
.tile:hover .external {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translate(2px, -2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Footer */
|
||||||
|
.footer {
|
||||||
|
margin: 3rem auto 0;
|
||||||
|
padding: 1.5rem;
|
||||||
|
text-align: center;
|
||||||
|
max-width: 720px;
|
||||||
|
border-radius: 14px;
|
||||||
|
background: var(--ifm-color-emphasis-100);
|
||||||
|
font-size: 0.95rem;
|
||||||
|
color: var(--ifm-color-emphasis-800);
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
.footer a {
|
||||||
|
color: var(--ifm-color-primary);
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.footer a:hover { text-decoration: underline; }
|
||||||
|
|
||||||
|
.empty {
|
||||||
|
padding: 3rem 1rem;
|
||||||
|
text-align: center;
|
||||||
|
color: var(--ifm-color-emphasis-600);
|
||||||
|
font-size: 0.95rem;
|
||||||
|
}
|
||||||
1091
website/src/data/userStories.json
Normal file
1091
website/src/data/userStories.json
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user