Components / structure

Feed

Carousel controls had no aria-controls until the upstream port found it; the same class of defect is what aria-posinset prevents here — a structure the eye reads and the ear cannot.

Rendered from the package · open on its own

Summary

A stream of articles that grows as the reader scrolls.

APG patternFeed
Built onnative elements
Statusstable
Since0.1.0
<Feed label="Recent activity" busy={loading} total={340} onLoadMore={fetchMore} articles={…} />

Anatomy

A data-se="feed" region holding articles, each a focus stop.

PartElementRequired
article<article>yes
article-meta<p>no
article-title<h3>yes
busy<div>no
label<span>yes

Properties

PropertyValuesDefaultWhat it does
articlesFeedArticle[]requiredThe loaded articles, oldest position first. Each is a focus stop and carries its position in the set.
labelstringrequiredNames the feed region.
busybooleannoneSets aria-busy so a reader is not dropped into half-rendered content.
totalnumbernoneTotal across the whole stream, or -1 when genuinely unknown.
offsetnumber0Index of the first rendered article within the whole feed. Required for a windowed or paged feed — without it article 101 announces as "1 of 340".
onLoadMore() => voidnoneCalled when the reader reaches the end. Set busy while the request is in flight, or a screen reader is read a half-inserted article.

States

StateExpressed as
Pressed:active
Disabled:disabled
Focused:focus-visible
Hover:hover

Behavior

Page Down and Page Up move between articles. onLoadMore fires when the reader reaches the end.

busy sets aria-busy while more is arriving, so a screen reader is not read a half-inserted article.

Accessibility

Each article carries aria-posinset and aria-setsize, because "12 of 340" is the only way a screen-reader user can tell where they are in something with no visible end.

Pass total={-1} when the length is genuinely unknown. That is the honest value, not a guess — a wrong setsize is worse than none, because a reader believes it.

A native <article> carries no role attribute, so the port asserts the role by computation rather than by looking for a string.

Keyboard

KeyResult
Page Down / Page UpMove to the next or previous article
Home / EndJump to the first or last loaded article

Roles, states and properties

AttributeSet by
aria-busyset at render
aria-describedbyset at render
aria-labelset at render
aria-labelledbyset at render
aria-posinsetset at render
aria-setsizeset at render
rolealways feed

Focus

Each article is a focus stop carrying its position and the set size, because "12 of 340" is the only way a reader with no visible end can tell where they are.

Screen reader

Not recorded as verified. Stage 4 of the readiness ladder — driving each component with VoiceOver and NVDA — is unstarted across the package, and no component should claim otherwise until docs/journal/ records the session that did it.

Contrast and target size

npm run check:contrast audits every color pair this component uses across all four skins, light and dark, and fails the build on a shortfall. Two waivers are on the record in docs/audit/DEFERRED.md.

It takes the touch floor under (pointer: coarse) — the visual size is unchanged on a desktop pointer.

Tested

  • test/apg/batch-a.test.jsx
  • test/apg/batch-b.test.jsx
  • test/apg/partials-sweep.test.jsx
  • test/browser/keyboard.spec.mjs
  • test/browser/upstream-natives.spec.mjs

Known gaps

_No upstream requirement is recorded as not applicable._

Appearance

TokenUsed for
--se-accentintent
--se-bgbackground, border-color
--se-borderborder, border-color
--se-border-widthborder, border-width
--se-control-hblock-size, inline-size
--se-control-h-lgblock-size, inline-size
--se-control-h-smblock-size, inline-size
--se-duration-instanttransition
--se-ease-standardtransition
--se-focus-coloroutline
--se-focus-offsetoutline-offset
--se-focus-widthoutline
--se-leading-headingline-height
--se-mix-downbackground, border-color
--se-mix-hoverbackground, border-color
--se-mix-lineborder-color
--se-mix-pressbackground
--se-mix-shadebackground, border-color
--se-mix-softbackground
--se-negativeintent
--se-positiveintent
--se-radius-lgborder-radius
--se-space-1margin
--se-space-3padding-inline
--se-space-4gap, padding, padding-inline
--se-space-5padding-inline
--se-surfacebackground
--se-surface-sunkbackground
--se-target-minblock-size, inline-size, min-block-size
--se-textcolor, intent
--se-text-disabledcolor
--se-text-mdfont-size
--se-text-mutedcolor
--se-text-on-intentcolor
--se-text-smfont-size
--se-text-xsfont-size
--se-tracking-headingletter-spacing
--se-weight-semiboldfont-weight

Motion

None. New articles appear; they do not slide in, because the reader may be mid-sentence above them.

Content

meta is the one line that places the article in time or context — 2 hours ago, Draft.

Usage

When not to use it. For a list a reader needs to get back to a known place in. A feed has no addresses; that is what Pagination is for.

Its keep-or-cut decision is on the v3 plan for week nine, and it has not been made.

Notes

Carousel controls had no aria-controls until the upstream port found it; the same class of defect is what aria-posinset prevents here — a structure the eye reads and the ear cannot.