Feed & Activity Items — State Matrix

Every visual permutation of the two list-item systems, ported from the real components. Feed items come from feed/feed_section/notifications/* on notification-list-item.vue + notification-record.vue + item-layout.vue. Activity items come from profile/activity-list/vf-activity.vue on record.vue. Toggle the axes at left — every specimen reacts at once, so you can compare like-for-like across types. Specimens are set in Inter (the real primary font); all labels and annotations are monospace so the two layers never blur.

Shared axes

Read status constants/notifications.js

User badge user-badge.vue

Mutually exclusive in that precedence — v-if / v-else-if chain.

Feed axes

Aggregation content-single / -many

Max 2 names ever render; the rest collapse to “and N others”.

Thumbnail item-layout has-image

On error the record sets hasImageFailed → image slot collapses.

item-layout size title line clamp

Deletable isLoggedIn

Follow button shouldShowButton

Avatar interaction isClickable

Service type ContentServiceType

Activity axes

Viewer name empty?
Snippet truncate(100)
Content alert processing_status

Diagnostics

Outlines the identifier + action columns record.vue renders but profile never fills.

Feed notification items

7 API types + 1 synthetic · notification-list-item.vue → notification-record.vue → item-layout.vue

Each type overrides a different combination of the record's identifier, title, image, and additional-details slots — that divergence is the bulk of what there is to harmonize. The identifier column is 32px minimum but holds five unrelated things across the set: an avatar, a filled like-badge, two different topic glyphs, a newsfeed/client image, and a trending illustration. Auditing all seven types confirms only follow embeds a control in the record, and it goes in the image slot — notification-record has no action slot at all, so the delete button is absolutely positioned over the row by the list-item wrapper instead.

Profile activity items

5 content types × 4 alert states · vf-activity.vue → record.vue

One component, one layout, no per-type branching in the markup — only the verb string changes. record.vue lays out three columns; activity fills exactly one of them.

Divergence matrix

what actually differs between the two systems today

Both systems render a bordered row with a timestamp, and both use components/timestamp.vue. Almost nothing else is shared — including the row wrapper itself.

AxisFeed itemActivity itemShared?