Skip to content

Commit 8afc773

Browse files
committed
feat: add image support to featured cards and refine layout
- Add image/imageAlt fields to overlay content model - Forms card now shows the phone mockup screenshot from solutions site - Cards with images use a sidebar layout (content left, image right) - Add section intro text framing the featured work - Refine card structure: title → summary → excerpt → tags → CTA
1 parent 699a9c8 commit 8afc773

8 files changed

Lines changed: 83 additions & 34 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: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,26 +14,36 @@ export function FeaturedCard({ entry, basePath }: { entry: CatalogEntry; basePat
1414
const summary = entry.overlay?.summary ?? entry.description ?? ''
1515
const highlights = entry.overlay?.highlights
1616
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 ?? ''
21+
const hasImage = Boolean(image)
1722

1823
return (
19-
<article class="featured-card">
20-
<div class="featured-card__header">
24+
<article class={`featured-card${hasImage ? ' featured-card--has-image' : ''}`}>
25+
<div class="featured-card__content">
2126
<h3 class="featured-card__title">
2227
<a href={href}>{title}</a>
2328
</h3>
29+
<p class="featured-card__summary">{summary}</p>
30+
{excerpt ? <p class="featured-card__excerpt">{raw(excerpt)}</p> : null}
2431
{highlights ? (
2532
<ul class="featured-card__tags">
2633
{highlights.map((h) => <li>{h}</li>)}
2734
</ul>
2835
) : null}
36+
<p class="featured-card__cta">
37+
<a href={href}>Explore {title} &rarr;</a>
38+
</p>
2939
</div>
30-
<div class="featured-card__body">
31-
<p class="featured-card__summary">{summary}</p>
32-
{excerpt ? <p class="featured-card__excerpt">{raw(excerpt)}</p> : null}
33-
</div>
34-
<p class="featured-card__cta">
35-
<a href={href}>Explore {title} &rarr;</a>
36-
</p>
40+
{image ? (
41+
<div class="featured-card__image">
42+
<a href={href} tabindex={-1} aria-hidden="true">
43+
<img src={image} alt={imageAlt} loading="lazy" />
44+
</a>
45+
</div>
46+
) : null}
3747
</article>
3848
)
3949
}

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

Lines changed: 43 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,27 @@
11
.featured-card {
22
display: grid;
3-
gap: var(--space-4);
4-
padding: var(--space-5) var(--space-6);
3+
gap: var(--space-5);
4+
padding: var(--space-6);
55
border-inline-start: 3px solid var(--color-accent);
66
border-radius: var(--radius-sm);
77
background: var(--color-surface);
88
box-shadow: var(--shadow-card);
9+
container-type: inline-size;
910
}
1011

11-
.featured-card__header {
12-
display: flex;
13-
flex-wrap: wrap;
14-
align-items: baseline;
12+
@container (min-width: 40rem) {
13+
.featured-card--has-image {
14+
grid-template-columns: 1fr 12rem;
15+
align-items: start;
16+
}
17+
}
18+
19+
/* ---- Content column ---- */
20+
21+
.featured-card__content {
22+
display: grid;
1523
gap: var(--space-3);
24+
align-content: start;
1625
}
1726

1827
.featured-card__title {
@@ -29,6 +38,20 @@
2938
color: var(--color-link-hover);
3039
}
3140

41+
.featured-card__summary {
42+
font-size: var(--step-0);
43+
color: var(--color-ink-subtle);
44+
font-weight: 500;
45+
max-inline-size: var(--measure-prose);
46+
}
47+
48+
.featured-card__excerpt {
49+
font-size: var(--step--1);
50+
color: var(--color-ink-subtle);
51+
line-height: 1.6;
52+
max-inline-size: var(--measure-prose);
53+
}
54+
3255
.featured-card__tags {
3356
display: flex;
3457
flex-wrap: wrap;
@@ -47,25 +70,8 @@
4770
white-space: nowrap;
4871
}
4972

50-
.featured-card__body {
51-
display: grid;
52-
gap: var(--space-3);
53-
max-inline-size: var(--measure-prose);
54-
}
55-
56-
.featured-card__summary {
57-
font-size: var(--step-0);
58-
color: var(--color-ink-subtle);
59-
font-weight: 500;
60-
}
61-
62-
.featured-card__excerpt {
63-
font-size: var(--step--1);
64-
color: var(--color-ink-subtle);
65-
line-height: 1.6;
66-
}
67-
6873
.featured-card__cta {
74+
margin-block-start: var(--space-2);
6975
font-size: var(--step--1);
7076
}
7177

@@ -79,3 +85,16 @@
7985
color: var(--color-link-hover);
8086
text-decoration: underline;
8187
}
88+
89+
/* ---- Image column ---- */
90+
91+
.featured-card__image {
92+
justify-self: center;
93+
}
94+
95+
.featured-card__image img {
96+
display: block;
97+
max-block-size: 20rem;
98+
inline-size: auto;
99+
border-radius: var(--radius-sm);
100+
}

src/design/layout.css

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,19 @@
6666
margin-inline: calc(-1 * var(--space-5));
6767
border-radius: var(--radius-md);
6868
}
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+
}
6977
.home-featured__list {
7078
display: flex;
7179
flex-direction: column;
7280
gap: var(--space-5);
81+
margin-block-start: var(--space-4);
7382
}
7483
.home-stats__grid,
7584
.work-index__list {

src/pages/home.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,12 @@ export function Home({
3737
</section>
3838

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

0 commit comments

Comments
 (0)