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
Mutually exclusive in that precedence — v-if / v-else-if chain.
Feed axes
Max 2 names ever render; the rest collapse to “and N others”.
On error the record sets hasImageFailed → image slot collapses.
Activity axes
Diagnostics
Outlines the identifier + action columns record.vue renders but profile never fills.
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.
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.
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.
| Axis | Feed item | Activity item | Shared? |
|---|