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. Pick a section below. Controls are split by what they do: design changes on the left are proposals, the axes on the right only select which existing state is on show, and each column lists only what actually drives the section you are in. Every specimen in view 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.

Design changes

Proposed deviations from what ships today — unlike the axes, which only document existing states. Controls here persist across tabs.

Blockquote style .vf-excerpt

Badge style base-badge.vue

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?

Loading

Replaces every specimen below with skeleton rows — Vuetify's v-skeleton-loader look, on request. Not a state either row component ships; see the CSS note by .vf-skeleton-bone.

Read state & badge

Feed-only — activity items have neither, which is itself two rows of the divergence matrix.

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.