The edge sits at every join between a dye or ink field and cotton, and nowhere else.
One rule, applied everywhere, is what makes it read as a system; the edge used once then never was the owner's first complaint.
Automated: scripts/site/system/compose.ts (inserts it) and scripts/site/system/check-pages.ts (verifies every built page)
Only the composer places the edge; templates never draw it.
If templates drew it, each would decide differently.
Automated: scripts/site/system/check.ts (no template renders .selvedge)
API
Prop
Type
Required
Default
What it does
thread
saffron | ink | indigo | madder | teal | context
No
"saffron"
The thread colour: saffron next to dye or ink fields, ink next to saffron; context lets the surrounding element set it (the footer).
Contract details
The full contract: identity, anatomy, variants, states, tokens, layout, motion, accessibility, content, tests
Identity
Aspect
Value
Id
woven-edge
Layer
Atoms
Category
brand
Owner
Site system lead
Status
stable
Version
2.0.0
Source
apps/showcase/site/system/atoms/woven-edge.ts (private repository; the contract and the render function are this one module)
Version
Date
Change
1.0.0
2026-10-25
The selvedge strip at the foot of page heads and the top of the footer.
2.0.0
2026-10-26
One rule: the edge sits at every join between a dye or ink field and cotton, and nowhere else; the composer places it (D-SITE-003).
Use when
Every join between a dye or ink field and cotton, in both directions. Why: A selvedge runs along both edges of a cloth; a field is a cloth, so it has an edge on each side (D-SITE-003).
Do not use when
As a divider between tiles or paragraphs. Why: Used as a divider it becomes a generic line and stops meaning 'cloth'.
Between two cotton sections or inside a field. Why: There is no join of field and cotton there, so the rule does not place one.
Anatomy
Part
What it is
root
span.selvedge, aria-hidden, 18 px tall: three rows of 12 px squares on cotton, drawn with CSS gradients.
Saffron shows on every dark ground and echoes the action colour.
ink thread
Next to a saffron field.
Saffron on saffron would vanish.
States
State
Rendered or not applicable
default
default
hover
Not applicable: The woven edge is not interactive, so it has no hover state.
focus-visible
Not applicable: The woven edge does not take focus; links and buttons inside it carry their own focus ring.
active
Not applicable: The woven edge cannot be pressed.
disabled
Not applicable: The woven edge has nothing to disable.
loading
Not applicable: The woven edge is rendered at build time and is complete when the page loads.
empty
Not applicable: The woven edge is not rendered without content; the builder skips it.
error
Not applicable: The woven edge shows no data that can fail.
current
Not applicable: The woven edge has no current item.
visited
Not applicable: The woven edge is not a link.
expanded
Not applicable: The woven edge does not open or close.
Tokens
Part
Token
root.height
--selvedge-height (18 px)
square.size
--selvedge-square (12 px)
row.height
--selvedge-row (6 px)
ground
--cotton
thread
--saffron, or --ink next to saffron
--woven-edge-edge-madder
var(--madder)
--woven-edge-height-selvedge-height
var(--selvedge-height)
--woven-edge-background-color-cotton
var(--cotton)
--woven-edge-background-image-selvedge-square
var(--selvedge-square)
--woven-edge-background-size-selvedge-row
var(--selvedge-row)
--woven-edge-background-position-selvedge-row
var(--selvedge-row)
--woven-edge-edge-indigo
var(--indigo)
--woven-edge-edge-saffron
var(--saffron)
--woven-edge-edge-teal
var(--teal)
--woven-edge-edge-ink
var(--ink)
--woven-edge-background-image-edge
var(--edge)
Layout
Aspect
Value
breakpoints
Required review at 390, 768, 1024 and 1440 px; execution is not implied by this declaration; full width at every size.
wrapping
Not text.
textExpansion
Not text.
Spacing and rhythm
Aspect
Value
internal
Three 6 px rows.
external
None: it sits exactly on the join.
rhythm
Placed by the composer between sections, never by a template.
Motion
Aspect
Value
None
The edge is cloth, and cloth does not move.
Runtime and reuse
Render-only element; mount any registered child enhancements separately. Preserve required Site CSS, fonts, responsive and locale rules from nativeElement.
Accessibility passport
Aspect
Value
role
presentation (aria-hidden)
name
None
keyboard
Not interactive.
focus
Not focusable.
screenReader
Silent.
contrast
Decorative; hidden in forced-colours mode.
target
Not a target.
Content
Slot
Length
Voice
decorative thread (no text)
No label, caption or child text; the existing thread enum or surrounding context supplies colour.
Silent decoration at a field/cotton join; explain the join in adjacent page content if needed.
Good: “A silent saffron edge between an indigo page head and cotton body.” · “An ink edge between a saffron call to action and the cotton footer.”
Bad: “A labelled divider announcing a new section.” · “A saffron edge between two cotton paragraphs.”
Japanese: The same silent, aria-hidden edge is used on Japanese pages. It has no translatable copy or accessible name; Japanese section titles remain in their owning templates.