Skip to main content
cngx-src documentation

CngxTimelineMarker

ComponentOnPushNo encapsulationv0.1.0WCAG AA

projects/common/timeline/marker.component.ts

Import#

import { CngxTimelineMarker } from '@cngx/common/timeline'

Description#

The dot on the timeline rail - a status-coloured slot host for whatever the consumer projects into it (nothing, a glyph, an icon, an avatar).

Purely decorative and marked aria-hidden: the status it paints is already carried semantically by the owning CngxTimelineItem, and announcing it twice is worse than not announcing it here at all. That also means colour is never this atom's only signal - the item's ARIA is.

Usable standalone. It ships its own token SET rules, so a marker dropped into a hand-rolled layout renders at the right size and tracks [data-density] with no timeline organism above it.

Bare dot

<cngx-timeline-marker status="done" />

With a projected glyph

<cngx-timeline-marker status="rejected">!</cngx-timeline-marker>

Pending

<cngx-timeline-marker status="active" busy />

Metadata#

Host#

Relationships

Index#

Inputs#

busy#
input()

Whether the marker pulses to signal work in flight - typically bound from an item's own [state] being pending.

The pulse is a box-shadow animation rather than a transform or a size change: the marker sits in a grid track the rail is aligned to, so anything that altered its box would drag the rail with it every frame. Suppressed under prefers-reduced-motion.

default false, { transform: coerceBooleanProperty }
input()

Where the event sits in the history. Drives the dot colour and, for upcoming, its hollow fill. Left unset the marker paints in the neutral rail colour.

HostBindings#

BindingExpression
[attr.data-status]status()
[class.cngx-timeline-marker--busy]busy()

The --cngx-timeline-* custom-property family, shared by every artifact in @cngx/common/timeline and re-used by the <cngx-timeline> organism in @cngx/ui/timeline. A pure token surface - registrations plus :root chains, no structural rules and no @scope.

It lives with the atoms rather than with the organism on purpose: a <cngx-timeline-item> dropped into a hand-rolled layout has to render complete and density-correct on its own. A token family that only resolved under a Level-4 host would make the molecule a fragment instead of a terminal unit.

Where the spacing tokens are, and why not here

This file carries no -gap / -padding registration. A spacing token is registered in the same stylesheet that SETs it from --cngx-space-*, at that component's own host:

  • --cngx-timeline-gap, --cngx-timeline-row-gap, --cngx-timeline-content-padding - timeline-item.component.css
  • --cngx-timeline-group-gap - the organism's stylesheet

Registering them here and SETting them there would put a registration one file away from its derivation, where the two can drift silently: a registered initial-value defeats every use-site var(token, fallback), so a token that loses its SET rule freezes at the literal and a nested [data-density] never reaches it. Keeping the pair together makes that failure impossible to introduce by accident, and is what the repo's density guard checks per file.

The sizes that remain here are affordances, not compactness, and are deliberately outside the density scale. --cngx-timeline-rail-inset in particular derives from --cngx-timeline-marker-size: the rail has to meet the marker's centre, and a density swap that moved one without the other would detach the rail from the dot. That derivation is applied at the row host (timeline-item.component.css), the nearest common ancestor of the marker and the connector - custom properties inherit downwards only, so deriving it on either sibling would never reach the other.

Colour and dark mode

Every colour chains to a --cngx-color-* foundation token, which already carries its own light and dark values. Dark mode therefore needs no per-token redefinition here - overriding --cngx-color-primary or the active colour scheme re-themes the timeline for free. The literal in each chain's tail is the leaf default for consumers running without the foundation stylesheet.

  • --cngx-timeline-connector-color -> --cngx-color-border
  • --cngx-timeline-surface -> --cngx-color-surface
  • --cngx-timeline-text-color -> --cngx-color-text
  • --cngx-timeline-muted-color -> --cngx-color-text-muted
  • --cngx-timeline-done-color -> --cngx-color-success
  • --cngx-timeline-active-color -> --cngx-color-primary
  • --cngx-timeline-rejected-color -> --cngx-color-danger
  • --cngx-timeline-upcoming-color -> --cngx-color-text-muted

Default visuals for CngxTimelineMarker - a round, status-coloured dot that centres whatever is projected into it.

Status modifiers

Driven by [data-status], set from the status Input:

  • done - filled in the success tone
  • active - filled in the primary tone, with a soft halo ring
  • upcoming - hollow, muted ring only, so the state reads without colour
  • rejected - filled in the danger tone

Busy pulse

.cngx-timeline-marker--busy animates box-shadow only. The marker shares a grid track with the connector rail, so animating size or transform would shift the rail's anchor every frame. Fully suppressed under prefers-reduced-motion.

Media

The dot clips to its circle, so a photo, a logo or a glyph can render inside it with no extra markup. The sizing contract has two halves and they are not the same:

  • Bare media (img / picture / svg) is sized here, as a percentage of the marker box, and therefore follows --cngx-timeline-marker-size on its own. Percentages rather than em: the scope pins font-size: 0.625em below, so an em-sized glyph would track that font-size and an enlarged marker would keep a 12px-era icon. An img fills the dot; an svg insets to --cngx-timeline-marker-glyph-size.
  • A projected CngxAvatar or CngxIcon sizes itself. Both pin --cngx-avatar-size / --cngx-icon-size on their own host from their size-variant classes, where nothing inherited from here can reach them, and a rule in this file naming .cngx-avatar--md would put a sibling atom's class in the marker's stylesheet at equal specificity - a source-order coin flip. So enlarging such a marker means setting --cngx-timeline-marker-size and the atom's own size.

--cngx-timeline-rail-inset derives from the marker size at the row host either way, so the rail meets an enlarged marker's centre with no extra wiring.

Density

--cngx-timeline-marker-size is a fixed affordance, not compactness, so it is not SET from the scale. It is the anchor --cngx-timeline-rail-inset derives from, but that derivation is applied at the row host in timeline-item.component.css: the marker and the connector are siblings, and a custom property set here would never reach the rail.

Index#

Layout

--cngx-timeline-rail-inset#<length>
Default value 6px

Distance the rail is pulled back from the edge of the marker cell so it meets the marker's centre. Affordance geometry, deliberately outside the density scale - it tracks --cngx-timeline-marker-size instead, and a density swap that moved it would detach the rail from the dot.

--cngx-timeline-marker-size#<length>
Default value 12px

Diameter of the marker dot.

--cngx-timeline-marker-glyph-size#<percentage>
Default value 60%

Size of a bare svg glyph projected into the marker, as a share of the marker box. A percentage rather than a length so it tracks --cngx-timeline-marker-size on its own: em would resolve against the marker's pinned font-size, which is not the dot's size.

Photos are not covered by this - an img or picture fills the dot edge to edge, because a cropped portrait wants no inset and a glyph does.

--cngx-timeline-item-inline-size#<length>
Default value 192px

Width of one row on the horizontal axis. Ignored in the vertical default, where a row takes the container's width.

An affordance, not compactness, so it is deliberately outside the density scale. It exists because a horizontal run has no sensible content-derived width: flex would size every card to its longest word and the axis would read as a ragged queue. Override per instance for denser or wider cards.

--cngx-timeline-connector-width#<length>
Default value 2px

Thickness of the connector rail.

--cngx-timeline-marker-ring-width#<length>
Default value 3px

Width of the halo ring drawn around a marker in the active status.

Surface

--cngx-timeline-connector-color#<color>
Default value oklch(0.88 0.005 250)

Colour of the connector rail in its default (unstated) status.

See: [[--cngx-color-border]]

--cngx-timeline-surface#<color>
Default value oklch(1 0 0)

Background the marker is punched out of, so a coloured rail passing behind the dot does not show through it.

See: [[--cngx-color-surface]]

Typography

--cngx-timeline-marker-fg#<color>
Default value oklch(1 0 0)

Foreground colour of a glyph projected into the marker.

--cngx-timeline-meta-size#*
Default value 0.8125rem

Font size for secondary text across the family - timestamps, inline errors, date headers, the refreshing tail. Registered here rather than with either consumer, because the row and the organism both read it and a registration in one is out of scope for the other.

--cngx-timeline-text-color#<color>
Default value oklch(0.2 0.01 250)

Primary text colour inside an item.

See: [[--cngx-color-text]]

--cngx-timeline-muted-color#<color>
Default value oklch(0.5 0.01 250)

Secondary text colour - timestamps, group headers, the refreshing tail.

See: [[--cngx-color-text-muted]]

Variant / Done

--cngx-timeline-done-color#<color>
Default value oklch(0.6 0.15 145)

Marker and rail colour for the done status.

See: [[--cngx-color-success]]

Variant / Active

--cngx-timeline-active-color#<color>
Default value oklch(0.66 0.19 50)

Marker and rail colour for the active status.

See: [[--cngx-color-primary]]

Variant / Upcoming

--cngx-timeline-upcoming-color#<color>
Default value oklch(0.5 0.01 250)

Marker and rail colour for the upcoming status. Pairs with a dashed rail, so the state survives a colour-blind reader.

See: [[--cngx-color-text-muted]]

Variant / Rejected

--cngx-timeline-rejected-color#<color>
Default value oklch(0.6 0.18 25)

Marker and rail colour for the rejected status, and for an item whose own [state] failed.

See: [[--cngx-color-danger]]

Motion

--cngx-timeline-pulse-duration#<time>
Default value 1.6s

Period of the busy pulse on a marker. Set to 0s to stop it without touching the rest of the family.