# days-since — self-computing elapsed-time counter

A number that drifts with time ("742 days and counting") is computed **in the
browser** from a fixed anchor date. It isn't baked into the HTML and bumped by a
republish cron. There's no scheduled run to miss, so the number can't go stale
while everything still reports green.

Pattern write-up: [patterns.gf.cx/days-since](https://patterns.gf.cx/days-since/)

## Use

```html
<span data-days-since="2024-09-15">742 days</span>
<script src="https://assets.gf.cx/days-since/days-since.js" defer></script>
```

- The text inside the span is the **no-JS fallback**. With JS it is overwritten
  on load. Keep it roughly current when you touch the page, but it never has to
  be exact.
- The anchor is **day 0**: the count is full days elapsed (`today − day0`), not
  an inclusive count. The anchor is read as local midnight.
- Every span on a page that shares one anchor shows the same count. They can't diverge.

| Attribute | Values | Default |
|---|---|---|
| `data-days-since` | `YYYY-MM-DD` anchor (day 0) | required |
| `data-unit` | `days` · `weeks` · `months` · `years` | `days` |

`data-unit` lets a page vary how it says the same span of time ("742 days",
"106 weeks", "24 months", "2 years") instead of repeating the day count.

A bad or future anchor leaves the fallback text untouched.

## Only live totals are dynamic

- **Live totals** ("742 *and counting*") get `data-days-since`.
- **Fixed markers** ("over 500 days *until she returned*") stay hard-coded.
  Making a past event dynamic silently rewrites history.

## Late-added nodes

Nodes inserted after load are picked up by a `MutationObserver`. One example is an
extra `<li>` cloned into the [made-with](../made-with/README.md) "Built with"
footer. So a counter can live inside the footer:

```html
<div data-made-with data-repo="xlab-nyc/happiness-gf-cx" data-surface="happiness.gf.cx">
  <ul><li>Day count · <span data-days-since="2024-09-15">742 days</span> since day 0
    (15 Sep 2024), computed live by <code>days-since.js</code></li></ul>
</div>
```

Manual re-render: `window.DaysSince.refresh(rootEl)`. Also exposed:
`DaysSince.elapsed('YYYY-MM-DD')` → integer days, and `DaysSince.format(days, unit)`.

## When NOT to use

When the number has to be in the HTML bytes themselves (an `og:image` card
headline, a crawler, a screenshot). Even then, don't add a bespoke cron. Add a
`data-days-since` pass to the shared build tooling so every surface gets it.

## Related

- [date-counter](../date-counter/README.md) is its future-facing cousin: a
  countdown to a deadline, with urgency colouring.
- [maintenance-timer](../maintenance-timer/README.md) counts down recurring intervals.

First use: [happiness.gf.cx/behaviour/the-1am-paragraph/](https://happiness.gf.cx/behaviour/the-1am-paragraph/),
where the no-contact counter was inline from 2026-07-07 and moved to the shelf on 2026-09-27.

v0.1.0 · 2026-09-27
