SaltiePulse
- Layer: Primitive
- Base: Custom SVG — no Ark UI dependency
- Motion: rAF-driven SVG attribute updates; skipped entirely under
prefers-reduced-motion: reduce
Lifecycle
Section titled “Lifecycle”Three phases carry a directional grammar: quiet water when waiting, outward ripples when thinking, inward convergence when an answer is surfacing. The primitive itself does not auto-advance — callers choose the phase.
Idle → Thinking → Emerging
One primitive, four sizes. Pick by where Saltie is rendered — inline chip, row marker, panel header, empty state.
xs · sm · md · lg
Contexts
Section titled “Contexts”Microcopy lives in the component. Callers pick phase + context and Saltie supplies the voice. general is poker-faced; reviewing, planning, researching tilt the language toward the task.
general / reviewing / planning / researching — thinking then emerging
In context
Section titled “In context”Chat thinking · titlebar dot · empty state
| Prop | Type | Default | Description |
|---|---|---|---|
phase | "idle" | "thinking" | "emerging" | required | Which phase to render. |
size | "xs" | "sm" | "md" | "lg" | "md" | 16 / 24 / 48 / 80 px. |
context | "general" | "reviewing" | "planning" | "researching" | "general" | Microcopy pool. Non-general pools cover thinking + emerging only; idle falls back to general. |
showLabel | boolean | false at xs, true else | Show copy next to the pulse. |
label | string | — | Override the resolved label entirely. Wins over pools. |
labelVariant | "short" | "long" | "short" at sm, "long" at md/lg | Only meaningful for context="general" — contextual pools are long-only. |
class | string | — | Extra class on the outer span. |
Design notes
Section titled “Design notes”- Direction is meaning. Outward = input dropped in. Inward = output surfacing. Never invert — the metaphor only works one way.
- Cycle times are fixed across themes. Saltie is 200 million years old. Idle breathes on 6s; thinking ripples on 2.2s; emerging converges in 2.4s then holds for 1.4s. A fast pulse reads anxious. A slow pulse reads considered.
- Stroke resolves through
var(--colors-accent), so themes work without extra plumbing. Switch delight / relight / gilt mid-render and the colour updates in one frame. - Reduced motion is detected via
matchMediaat mount; underprefers-reduced-motion: reducethe component renders a static settled pose and never startsrequestAnimationFrame. - aria-label is phase-only (
"Saltie idle"/"Saltie thinking"/"Saltie answering"). Visible copy is flavour; screen readers get intent.
Microcopy pools
Section titled “Microcopy pools”Each context supplies a long pool of one-liners. Pool selection is deterministic — a minute-granularity hash of context + phase + size, so the quip is stable for the duration of one phase but varies across mounts.
- General carries both a single-word short default (
"Lurking."/"Chewing."/"Surfacing.") and a long pool. - Reviewing is close-up, in the jaws:
"Chewing on it.","Reading the grain.","Checking the bones." - Planning is wide-angle:
"Lining up the shot.","Picking the angle.","Mapping the shallows." - Researching is going deep:
"Sounding the depths.","Consulting the mangroves.","Waking the council."
Rare opt-in
Section titled “Rare opt-in”SALTIE_PULSE_RARE_THINKING_GENERAL exports the "Letting it marinate." quip as a named constant. It is not in any default pool — pass it via label when you want the flavour without polluting the poker-faced default. Keep it rare.
Plane 1 Constraints
Section titled “Plane 1 Constraints”- No background, no border on the component itself — composition sits with the caller.
- No color shifts on errors or success; pulse carries only presence and phase.
- No CSS keyframes — the rAF engine writes SVG attributes directly so the timings match the reference exactly and cleanup is deterministic.
Token Dependencies
Section titled “Token Dependencies”| Token | Source |
|---|---|
--colors-accent | Stroke + fill across all phases |
text.secondary | Label colour |
font.mono | Label typeface |