Skip to content

Commit 4caed60

Browse files
authored
Merge pull request #8 from flexion/worktree-worktree-featured-content-design
Redesign featured section as stacked content cards
2 parents 478e711 + 33ce0d1 commit 4caed60

8 files changed

Lines changed: 90 additions & 26 deletions

File tree

content/work/forms.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ highlights:
55
- WCAG 2.1 AA conformant
66
- Multi-step with save and resume
77
- Agency-agnostic, works with any back-end
8+
image: /assets/forms-phone.png
9+
imageAlt: A mobile phone displaying an accessible government form built with Flexion Forms
810
related:
911
- forms-lab
1012
---

src/catalog/overlays.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ export async function loadOverlay(path: string): Promise<Overlay | null> {
2121
summary: stringOrUndefined(frontMatter.summary),
2222
highlights: stringArrayOrUndefined(frontMatter.highlights),
2323
related: stringArrayOrUndefined(frontMatter.related),
24+
image: stringOrUndefined(frontMatter.image),
25+
imageAlt: stringOrUndefined(frontMatter.imageAlt),
2426
body: body ? (marked.parse(body, { async: false }) as string) : undefined,
2527
}
2628
}

src/catalog/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ export type Overlay = {
3636
summary?: string
3737
highlights?: string[]
3838
related?: string[]
39+
image?: string
40+
imageAlt?: string
3941
body?: string
4042
}
4143

src/design/assets/forms-phone.png

549 KB
Loading

src/design/components/featured-card/index.tsx

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,43 @@
11
import type { CatalogEntry } from '../../../catalog/types'
22
import { url } from '../../../build/config'
3+
import { raw } from 'hono/html'
4+
5+
/** Extract first <p>…</p> from rendered HTML body */
6+
function firstParagraph(html: string): string | null {
7+
const match = html.match(/<p>([\s\S]*?)<\/p>/)
8+
return match ? match[1] : null
9+
}
310

411
export function FeaturedCard({ entry, basePath }: { entry: CatalogEntry; basePath: string }) {
512
const href = url(`/work/${entry.name}/`, basePath)
613
const title = entry.overlay?.title ?? entry.name
714
const summary = entry.overlay?.summary ?? entry.description ?? ''
815
const highlights = entry.overlay?.highlights
16+
const excerpt = entry.overlay?.body ? firstParagraph(entry.overlay.body) : null
17+
const image = entry.overlay?.image
18+
? url(entry.overlay.image, basePath)
19+
: null
20+
const imageAlt = entry.overlay?.imageAlt ?? ''
921

1022
return (
1123
<article class="featured-card">
1224
<h3 class="featured-card__title">
1325
<a href={href}>{title}</a>
1426
</h3>
15-
{summary ? <p class="featured-card__summary">{summary}</p> : null}
27+
{image ? (
28+
<a class="featured-card__image" href={href} tabindex={-1} aria-hidden="true">
29+
<img src={image} alt={imageAlt} loading="lazy" />
30+
</a>
31+
) : null}
32+
<p class="featured-card__summary">{summary}</p>
33+
{excerpt ? <p class="featured-card__excerpt">{raw(excerpt)}</p> : null}
1634
{highlights ? (
17-
<ul class="featured-card__highlights">
35+
<ul class="featured-card__tags">
1836
{highlights.map((h) => <li>{h}</li>)}
1937
</ul>
2038
) : null}
2139
<p class="featured-card__cta">
22-
<a href={href}>Learn more &rarr;</a>
40+
<a href={href}>Explore {title} &rarr;</a>
2341
</p>
2442
</article>
2543
)

src/design/components/featured-card/styles.css

Lines changed: 41 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,15 @@
11
.featured-card {
2-
padding: var(--space-5);
3-
border: 1px solid var(--color-surface-alt);
4-
border-block-start: 3px solid var(--color-accent);
5-
border-radius: var(--radius-md);
2+
padding: var(--space-6);
3+
border-inline-start: 3px solid var(--color-accent);
4+
border-radius: var(--radius-sm);
65
background: var(--color-surface);
76
box-shadow: var(--shadow-card);
8-
display: grid;
9-
gap: var(--space-3);
10-
align-content: start;
117
}
128

139
.featured-card__title {
14-
font-size: var(--step-1);
10+
font-size: var(--step-2);
1511
line-height: 1.2;
12+
margin-block-end: var(--space-3);
1613
}
1714

1815
.featured-card__title a {
@@ -24,28 +21,54 @@
2421
color: var(--color-link-hover);
2522
}
2623

24+
.featured-card__image {
25+
float: inline-end;
26+
margin-inline-start: var(--space-5);
27+
margin-block-end: var(--space-3);
28+
}
29+
30+
.featured-card__image img {
31+
display: block;
32+
max-block-size: 16rem;
33+
inline-size: auto;
34+
border-radius: var(--radius-sm);
35+
}
36+
2737
.featured-card__summary {
38+
font-size: var(--step-0);
2839
color: var(--color-ink-subtle);
40+
font-weight: 500;
41+
max-inline-size: var(--measure-prose);
42+
margin-block-end: var(--space-3);
43+
}
44+
45+
.featured-card__excerpt {
2946
font-size: var(--step--1);
47+
color: var(--color-ink-subtle);
48+
line-height: 1.6;
49+
max-inline-size: var(--measure-prose);
50+
margin-block-end: var(--space-3);
3051
}
3152

32-
.featured-card__highlights {
53+
.featured-card__tags {
54+
display: flex;
55+
flex-wrap: wrap;
56+
gap: var(--space-2);
3357
list-style: none;
3458
padding: 0;
35-
display: flex;
36-
flex-direction: column;
37-
gap: var(--space-1);
38-
font-size: var(--step--1);
59+
margin: 0 0 var(--space-3);
3960
}
4061

41-
.featured-card__highlights li::before {
42-
content: "\2713\0020";
43-
color: var(--color-tier-active);
44-
font-weight: 700;
62+
.featured-card__tags li {
63+
font-size: var(--step--1);
64+
color: var(--color-ink-subtle);
65+
padding: var(--space-1) var(--space-3);
66+
background: var(--color-surface-highlight);
67+
border-radius: var(--radius-sm);
68+
white-space: nowrap;
4569
}
4670

4771
.featured-card__cta {
48-
margin-block-start: auto;
4972
font-size: var(--step--1);
5073
}
5174

src/design/layout.css

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,20 +66,32 @@
6666
margin-inline: calc(-1 * var(--space-5));
6767
border-radius: var(--radius-md);
6868
}
69-
.home-featured__grid,
69+
.home-featured__header {
70+
display: grid;
71+
gap: var(--space-2);
72+
}
73+
.home-featured__intro {
74+
font-size: var(--step-0);
75+
color: var(--color-ink-subtle);
76+
}
77+
.home-featured__list {
78+
display: flex;
79+
flex-direction: column;
80+
gap: var(--space-5);
81+
margin-block-start: var(--space-4);
82+
}
7083
.home-stats__grid,
7184
.work-index__featured-grid {
7285
display: grid;
7386
gap: var(--space-5);
7487
container-type: inline-size;
7588
}
7689
@container (min-width: 48rem) {
77-
.home-featured__grid { grid-template-columns: repeat(2, 1fr); }
7890
.home-stats__grid { grid-template-columns: repeat(3, 1fr); }
7991
.work-index__featured-grid { grid-template-columns: repeat(2, 1fr); }
8092
}
8193
@container (min-width: 72rem) {
82-
.home-featured__grid { grid-template-columns: repeat(4, 1fr); }
94+
.work-index__list { grid-template-columns: repeat(3, 1fr); }
8395
}
8496
.work-list {
8597
list-style: none;

src/pages/home.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,13 @@ export function Home({
3636
</section>
3737

3838
<section class="home-featured" aria-labelledby="featured-heading">
39-
<h2 id="featured-heading">Featured labs</h2>
40-
<div class="home-featured__grid">
39+
<div class="home-featured__header">
40+
<h2 id="featured-heading">Featured</h2>
41+
<p class="home-featured__intro">
42+
Products and tools we actively steward — built for government, shared with everyone.
43+
</p>
44+
</div>
45+
<div class="home-featured__list">
4146
{featured.map((entry) => (
4247
<FeaturedCard entry={entry} basePath={config.basePath} />
4348
))}

0 commit comments

Comments
 (0)