From fd51ee82d4a2a7ba8a24de83fc43883bfc92e9e5 Mon Sep 17 00:00:00 2001 From: Matt McKay Date: Wed, 30 Sep 2026 22:22:45 +1000 Subject: [PATCH] activity: add the "Latest activity" strip to the home page Show the three newest Activity rows (site.data.activity_view.strip) under the home page's news cards, each row one link: to the release, the live site or, for a merged row, its day on /activity/. The strip is written inline in _layouts/home.html with the shared Activity includes (meta.html and title.html), inside mathjax_ignore, with every field escaped. Without data there is no strip. Below 1200px it is a stacked list with 44px rows. From 1200px it is one line: each row gets an equal share of it, capped at its content width, and a row's minimum is its icon, date, tag and one letter of its title. So only titles are cut, and a translation's language or a group's label always shows whole. The news cards' dates now read "Jul 17, 2026", as on /news/, and "View all news" goes full width below 768px. README's News and Activity section adds the home page to where Activity appears. Part of #279. Co-Authored-By: Claude Opus 5.5 --- README.md | 2 +- _layouts/home.html | 31 +++++++++- assets/main.scss | 151 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 180 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e6a9bbf..d038bff 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ The site has two streams of updates. The rule of thumb: **people write News, the **Promotion is a human decision.** When a release or a batch of lecture changes deserves wider attention, someone writes a News post that links to it. -These definitions decide which stream an item belongs to, not where it is shown. News posts are stored in `_posts/` and Activity entries in `_data/activity/` (schema below). Which pages display each stream is a design choice: today News appears on `/news/`, the home page and `feed.xml`, and Activity on `/activity/`. +These definitions decide which stream an item belongs to, not where it is shown. News posts are stored in `_posts/` and Activity entries in `_data/activity/` (schema below). Which pages display each stream is a design choice: today News appears on `/news/`, the home page and `feed.xml`, and Activity on `/activity/` and, as its three newest rows, on the home page. ### Activity data diff --git a/_layouts/home.html b/_layouts/home.html index f45396e..e09e100 100755 --- a/_layouts/home.html +++ b/_layouts/home.html @@ -232,8 +232,7 @@

Latest News

- {% assign date_format = site.minima.date_format | default: "%-d %b %Y" %} -

{{ post.date | date: date_format }} +

{{ post.date | date: "%b %-d, %Y" }} {% if post.tag %} {% for tag in post.tag %} {{ tag }} @@ -248,7 +247,33 @@

View all news

+ {%- comment -%} + The "Latest activity" strip: the 3 newest Activity rows, each one link. Nothing in the + view is escaped, so every field is escaped here. Below 1200px a stacked list; from + 1200px one line, in which only titles are cut. + {%- endcomment -%} + {%- assign activity_strip = site.data.activity_view.strip -%} + {%- if activity_strip.size > 0 %} + + {%- endif %} + +

View all news

diff --git a/assets/main.scss b/assets/main.scss index ab73df6..8f0326d 100755 --- a/assets/main.scss +++ b/assets/main.scss @@ -2089,6 +2089,157 @@ body.sponsor-bar-visible.internal main { } } +// The home page's "Latest activity" strip: the 3 newest rows, each one link. Below 1200px, a +// stacked list with 44px rows; from 1200px, one line in which only titles are cut. +.activity-strip { + margin-top: 24px; + padding: 14px 16px 0; + border: 1px solid #dbe8f5; + border-radius: 8px; + background: #eef4fb; + a:focus-visible { + outline: 2px solid $color-link; + outline-offset: 2px; + } +} + +.activity-strip__label { + display: flex; + align-items: center; + gap: 8px; + margin: 0 0 6px; + font-size: 15.2px; + font-weight: 700; + color: $color-dark; + .bi { + color: $color-link; + } +} + +.activity-strip__rows { + margin: 0; + padding: 0; + list-style: none; + li { + padding: 8px 0; + border-top: 1px solid #dbe8f5; + } +} + +// The icon, date and tag on the first line and the title under them. In the HTML the title +// comes before the tag: the order in which a screen reader reads the link. On hover only the +// title changes. +.activity-strip__link { + display: flex; + flex-wrap: wrap; + align-items: center; + column-gap: 6px; + min-height: 44px; + font-size: 13.6px; + line-height: 1.4; + &, + &:hover { + color: inherit; + text-decoration: none; + } + .activity-meta__icon { + color: $color-link; + } +} + +.activity-strip__title { + order: 1; + flex-basis: 100%; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + overflow: hidden; + margin-top: 3px; + font-size: 15.2px; + font-weight: 700; + color: $color-dark; + .activity-strip__link:hover & { + color: $color-visited; + text-decoration: underline; + } +} + +.activity-strip__all { + display: flex; + align-items: center; + min-height: 48px; + border-top: 1px solid #dbe8f5; + font-size: 14.4px; + font-weight: 700; +} + +// One line. Each row gets an equal share of it but no more than its content needs, so a short +// title leaves its space to a long one. A row's minimum is its min-content: the icon, date and +// tag, and one letter of the title, which can wrap anywhere and is clamped to one line. So only +// titles are ever cut. (The handoff's min-width: 0 on each row and nowrap title keep a tag +// whole only while the line has width to spare.) +@media (min-width: 1200px) { + .activity-strip { + display: flex; + align-items: center; + gap: 20px; + min-height: 48px; + padding: 0 18px; + border-color: $color-border; + background: $color-light; + } + .activity-strip__label { + flex: none; + margin: 0; + font-size: 12px; + letter-spacing: 0.06em; + text-transform: uppercase; + color: #666; + .bi { + font-size: 14px; + } + } + .activity-strip__rows { + display: flex; + flex: 1 1 auto; + min-width: 0; + color: #666; // the row links inherit it + li { + flex: 1 1 0; + max-width: max-content; + padding: 0 20px; + border-top: 0; + border-left: 1px solid $color-border; + } + } + .activity-strip__link { + flex-wrap: nowrap; + align-items: baseline; + gap: 8px; + min-height: 0; + font-size: 13px; + white-space: nowrap; + .activity-meta__icon { + font-size: 12px; + } + } + .activity-strip__title { + order: 0; + flex: 0 1 auto; + margin: 0; + -webkit-line-clamp: 1; + white-space: normal; + word-break: break-all; + font-size: 14px; + } + .activity-strip__all { + flex: none; + min-height: 0; + border: 0; + font-size: 13.6px; + } +} + /* Respect a reduced-motion preference for the card and button transitions */ @media (prefers-reduced-motion: reduce) { .qe-card,