# un17-brand

Shared brand assets for the UN17 Village community sites. This repo is the
canonical source; consumers load the published `@un17/logo` package from a CDN
at a pinned version.

## `<un17-logo>` — the animated UN17 Village logo

`un17-logo.js` defines a dependency-free web component with the animated
"UN17 Village" mark: the plant doodle, the hand-drawn "UN17" with its blurred
ghost echo, the "Village" write-on animation, and the self-drawing underline.
Ported from the original implementation on resources.un17hub.com.

### Usage

Load the package from a CDN at a pinned version (bump the version to update):

```html
<script src="https://cdn.jsdelivr.net/npm/@un17/logo@1.0.0/un17-logo.js" defer></script>

<un17-logo></un17-logo>
```

The host page must load the display fonts, e.g. from Google Fonts:

```
https://fonts.googleapis.com/css2?family=Amatic+SC:wght@700&family=Caveat:wght@600&display=swap
```

React apps that bundle can `import "@un17/logo"` once (registers the element as a
side effect) and then use the tag directly in JSX — custom elements are valid JSX
elements.

### Attributes

| Attribute                   | Meaning                                                                                                           |
| --------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `variant="header"｜"footer"` | Color/size preset. `header` (default): sage, 32px mark, 170×46 wordmark. `footer`: rust, smaller, slightly muted. |
| `color`                     | Paint for the mark and lettering. Any CSS color, including `inherit` to take the surrounding text color.          |
| `ghost`                     | Fill for the blurred "UN17" echo behind the lettering (tune it for dark backgrounds).                             |
| `mark-size`, `width`, `height` | Numeric overrides for the doodle size and the wordmark's rendered box.                                         |
| `static`                    | Present: skip the write-on animation. (`prefers-reduced-motion` always skips it.)                                 |

Accessibility: the host element gets `role="img"` and `aria-label="UN17
Village"` automatically unless you set your own.

## Previewing the logo

`preview.html` renders `un17-logo.js` from the working tree in every variant,
state, and a few attribute overrides (custom size, color override, light and
dark backgrounds). It loads the Amatic SC / Caveat fonts, so it shows the mark
as consumers see it.

- **Locally:** serve the repo root and open the page, so the relative
  `./un17-logo.js` and the fonts both load:

  ```
  python3 -m http.server 8000
  # then open http://localhost:8000/preview.html
  ```

- **On a PR:** the [logo-preview workflow](.github/workflows/preview.yml)
  renders `preview.html` in headless Chromium on every PR that changes
  `un17-logo.js` or `preview.html`, and posts (or updates) a single sticky
  comment with the screenshot — so you can eyeball the change without checking
  the branch out.

  The workflow uploads the screenshot through the same S3 script the repo
  already uses, so it needs these **Actions secrets** (Settings → Secrets and
  variables → Actions): `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`,
  `AWS_REGION`, and `PR_SCREENSHOT_S3_BUCKET`. If they're absent the job posts a
  short note asking for them and still passes, so it never blocks a PR.
  Pull requests from forks are skipped, because Actions secrets aren't exposed
  to them.

## Consuming the package

The logo is published to public npm as [`@un17/logo`](https://www.npmjs.com/package/@un17/logo).
Buildless static sites load it from a CDN at a pinned version; there's nothing to
vendor and no SHA to record.

```html
<script src="https://cdn.jsdelivr.net/npm/@un17/logo@1.0.0/un17-logo.js" defer></script>
```

Pin an exact version (`@1.0.0`), not a range or `@latest`, so a consumer never
picks up a change it didn't opt into. To update, bump the version in the URL.
Sites that bundle can instead `npm install @un17/logo` and `import "@un17/logo"`.

Known consumers: `un17hub` (homepage header + loppemarked page),
`un17-resources` (candidate — currently ships its own React original).

### Consumer update notifications

When a new version publishes, the release workflow opens a "Bump `@un17/logo` to
`vX.Y.Z`" GitHub issue in each repo listed in the `CONSUMER_REPOS` **repository
variable** (a comma- or newline-separated list of `owner/repo`), so consumers get
a nudge to pin the new version. It skips a repo that already has an open issue for
that version, and a per-repo failure is a warning — it never fails the release.
This needs a `CONSUMER_ISSUE_TOKEN` secret with **Issues: write** on the listed
repos (the release `RELEASE_TOKEN` is scoped to this repo only). Leave
`CONSUMER_REPOS` unset to turn the notifications off.

## Changing the logo

Edit `un17-logo.js` here first, then merge to `main` — the release is automatic:

1. Make the change and validate it (see [Validation](AGENTS.md) — inspect both
   variants in a browser).
2. Open a PR and merge it to `main`. The
   [publish-on-merge workflow](.github/workflows/publish.yml) publishes the
   current `version` in `package.json` to npm, tags the commit `vX.Y.Z`, then
   **auto-increments the patch** in `package.json` and pushes that bump back to
   `main` (as a `[skip ci]` commit). No local `npm publish`, and you don't bump
   the version by hand for a patch release.
3. Consumers bump the pinned version in their CDN URL when they're ready.

Because the workflow pre-bumps the patch, `version` in `package.json` always
holds the **next** (not-yet-published) version. For a **minor or major** release,
hand-edit `version` in `package.json` in your PR before merging — the workflow
publishes whatever is there, then resumes auto-bumping the patch from it.

The workflow only runs when `un17-logo.js` or `package.json` changes, so a
docs-only merge (README, AGENTS) doesn't cut a new version. The publish step is
also idempotent: if the current `version` is already on npm (e.g. a re-run), it
skips the publish and tag rather than failing. The job fails cleanly if the
`NPM_TOKEN` secret is missing.

Never edit a consumer's copy or ask them to hand-patch the file — publishing a new
version is the only propagation path. That's the drift the loppemarked page's old
inline SVG fell into, and what the package model exists to prevent.

### Publishing for the first time

The initial `1.0.0` publish is manual and needs the `@un17` scope to exist on
npm and a maintainer logged in with publish rights (`npm login`, then
`npm publish`). Before the first automated release, add an npm automation token
(with publish rights on the `@un17` scope) as the `NPM_TOKEN` repository secret
so the workflow can authenticate. After that, later publishes go through the
merge-to-publish flow above.

The workflow pushes the auto-bump commit and tag back to `main` using the
`RELEASE_TOKEN` secret — a `contents: write` token (a fine-grained PAT or a
GitHub App token) whose identity is on the `main` ruleset's **bypass list**, so
the push isn't rejected by "require a pull request." The built-in `GITHUB_TOKEN`
can't be a ruleset bypass actor, which is why a separate token is required. If
`RELEASE_TOKEN` is missing or its identity isn't on the bypass list, the publish
still succeeds but the version bump won't land.
