# Logo and brand

Nine sample bars and the line of their own mean, why none of it is drawn at partial opacity, and the contrast each tone measures against its page.

Source: https://docs.jmrp.io/mikroscope/about/brand/

The mikroscope mark is nine sample bars and the line of their own mean, drawn in
two solid tones per theme. Below: what it says, how it is generated, and the
contrast of every tone against the background it is drawn on, with the cost of
that choice beside it.

![The mikroscope mark: nine sample bars, three of them standing above the flat line of their own mean](https://raw.githubusercontent.com/jmrplens/mikroscope/main/site/src/assets/mark-inline.svg)

## Concept

The mark is geometry, not a drawing: nine sample bars whose envelope is a
burst, crossed by the flat line of their own mean. That is the whole claim of
the project in one shape: a one-second average reports the line, and
sub-second sampling is what resolves the spike standing over it.

The burst is asymmetric the way a real one is: three quiet samples, a fast
rise, the peak, a slower fall, three quiet again. The peak sits in the middle
of an odd number of bars, so the mark balances on its own centre.

## Mean line

The generator draws the line at the arithmetic mean of the heights it was
given, not at a number chosen to look right, and a bar takes the loud tone
exactly when it stands above that mean. So the sentence the mark makes is one
the code enforces: three of the nine samples are above their own average, and
they are the three the eye goes to. `TestTheLineSitsAtTheMeanOfTheBars` fails
the build if the drawing and the arithmetic drift apart.

## Palette

Measured against the background each is drawn on, as WCAG 2.2 defines contrast:

| Theme | Background | Above the mean        | At or below it, and the line | Apart  |
| ----- | ---------- | --------------------- | ---------------------------- | ------ |
| Dark  | `#0e1316`  | `#fbbf24` **11.20:1** | `#c2740a` **5.16:1**         | 2.17:1 |
| Light | `#ffffff`  | `#633009` **10.73:1** | `#b45309` **5.02:1**         | 2.14:1 |

One palette per theme is not a refinement; it is the only way the mark is
legible in both. An amber light enough to read on the near-black page reads
1.67:1 on white (`#fbbf24`), and one dark enough for white disappears into the
dark page. On white, the sample standing above the
mean is the darker tone, so the mark reads the same way round in both themes.

`TestEveryToneClearsAAInItsOwnTheme` recomputes the ratios from the hex on
every run and fails the build if any tone reads under 4.5:1 on its own
background, or if a theme's two tones are less than 2.0:1 apart. A tone edited
without checking it fails the build rather than shipping.
`TestNothingInTheMarkDependsOnOpacity` guards the other half of the decision.

## No opacity

A quiet bar drawn as the loud colour at a low opacity reads well and fails.
Composited over the page, 0.28 of `#f59e0b` gives **1.73:1** on the dark
background and 0.42 of `#a16207` gives **1.80:1** on white, where AA asks
4.5:1. Reaching 4.5 by raising the alpha needs 0.68 in the dark theme and 0.96
in the light one, at which point a quiet bar is a loud bar and the one
distinction the mark exists to make is gone. So the split is two solid tones.

That costs punch. Solid against solid gives about **2.1:1** between the two
groups; drawing the quiet bar at 0.28 (0.42 on white) separates them by about
5:1 in the dark theme and 2.7:1 in the light one, at alphas that read 1.73:1 and
1.80:1 against their own page and so fail AA. What it buys is that nothing in
the mark composites against a background this repository does not control (a
README on GitHub, an `og:image` in a chat client, a favicon over browser
chrome), so every number in the table is the number the reader actually gets.

## Contrast rule

WCAG 1.4.11 asks **3:1** of a graphical object, and 1.4.3 exempts logotypes
from any minimum at all. 4.5:1 on every bar is a house rule stricter than the
standard, chosen by the project's maintainer, José Manuel Requena Plens, over
the smaller change of lifting the alphas to 3:1.

## Amber

Amber also means _threshold_ on this project's dashboards, where a panel turns
orange before it turns red. The two do not collide literally (the panels use
Grafana's own named colours and never these hexes), but a reader who has learnt
"amber means look at this" on a dashboard is being asked to read the same hue
as the project's own mark. That is the cost of the choice, made knowingly.

## Bar heights

The bar heights are not a real capture. A burst mikroscope measured peaked at
736 packets in one 20 ms sample against a median of 28, and 26× is a range no single square
renders: the quiet samples collapse to dots, or a log scale flattens the very
spike the mark exists to show. The heights in the mark are proportions of the
drawn height.

## Favicon

Nine bars at sixteen pixels is mush, so the favicon drops to five and keeps the
spike standing over the line, which is the part that carries the meaning.

The site's `favicon.svg` is the only icon with no ground of its own. It carries
both palettes and switches on the reader's own `prefers-color-scheme`, the
signal browser chrome itself follows, so it draws `#fbbf24`/`#c2740a` over a
dark chrome and `#633009`/`#b45309` over a light one. Every raster brings its
own `#0e1316` ground instead, because an `.ico` has no way to ask.

## Generated files

The mark lives as a generator, [`cmd/gen_brand/`](https://github.com/jmrplens/mikroscope/tree/main/cmd/gen_brand), rather than as a folder of
hand-drawn files, because changing the palette or the bar count is then one
edit instead of nine in each of a dozen files. It is a build-time tool and is
not one of the two released binaries. From the root of the repository:

```sh
go run ./cmd/gen_brand mark -out brand          # the mark and the favicon, per theme
go run ./cmd/gen_brand compose -out brand       # the banner, the social image and the og:image
go run ./cmd/gen_brand icons -out site/public   # the favicon, the touch icons and the web app manifest
```

`mark` is pure text. `compose` reads the three `bg-*.png` backgrounds from the
directory it writes to and shells out to `rsvg-convert` for the PNGs that ship.
`icons` shells out to `rsvg-convert` and to ImageMagick for the `.ico`, and is
the only one that writes outside [`brand/`](https://github.com/jmrplens/mikroscope/tree/main/brand). Every coordinate is written with two
decimals, rounded half to even, so the files are meant to reproduce byte for
byte on any machine; `TestMarkIsByteForByteReproducible` checks that two runs on
the same host give the same four mark and favicon files.

| File                                                   | Where          | Subcommand | What it is                                                                                            |
| ------------------------------------------------------ | -------------- | ---------- | ----------------------------------------------------------------------------------------------------- |
| `mark-dark.svg`, `mark-light.svg`                      | `brand/`       | `mark`     | The mark, one per theme                                                                               |
| `favicon-dark.svg`, `favicon-light.svg`                | `brand/`       | `mark`     | The five-bar variant, one per theme                                                                   |
| `mark-inline.svg`                                      | `brand/`       | `mark`     | The mark for a page that inlines it: the loud tone is `currentColor`, the quiet one `--ms-mark-quiet` |
| `favicon-inline.svg`                                   | `brand/`       | `mark`     | The favicon for a page that inlines it, the inline twin of `favicon.svg`                              |
| `banner.svg` and `.png`                                | `brand/`       | `compose`  | 1280×320, for the README                                                                              |
| `social.svg` and `.png`                                | `brand/`       | `compose`  | 1280×640, the repository social preview                                                               |
| `og.svg` and `.png`                                    | `brand/`       | `compose`  | 1200×630, the documentation `og:image`                                                                |
| `background.png`                                       | `brand/`       | none       | The generated field the three compositions crop from                                                  |
| `bg-banner.png`, `bg-social.png`, `bg-og.png`          | `brand/`       | none       | Those crops, which `compose` reads                                                                    |
| `favicon.svg`                                          | `site/public/` | `icons`    | Both palettes, switching on `prefers-color-scheme`                                                    |
| `site.webmanifest`                                     | `site/public/` | `icons`    | The web app manifest: names the 192, 512 and maskable rasters, with relative URLs                     |
| `favicon.ico`                                          | `site/public/` | `icons`    | Three drawings, at 16, 32 and 48 px, rather than one scaled three ways                                |
| `apple-touch-icon.png`, `icon-192.png`, `icon-512.png` | `site/public/` | `icons`    | 180, 192 and 512 px, each drawn at its own size                                                       |
| `icon-maskable-512.png`                                | `site/public/` | `icons`    | Inset further, to sit inside the circle 80 % of the icon across that a launcher may crop to           |

Every page's head links `favicon.svg`, `favicon.ico`, `apple-touch-icon.png` and `site.webmanifest`.
The `.ico` link says `sizes="32x32"`, which keeps Chromium on the SVG alone: headless Chromium 153
requested only `favicon.svg`. The manifest names `icon-192.png`, `icon-512.png` and
`icon-maskable-512.png` by URLs relative to itself, and leaves out `favicon.svg`, the one icon with
no ground of its own, since whatever lands on a home screen brings its own. It carries no `id`,
which is resolved against the origin of `start_url` rather than against the manifest: Chromium 153
resolved `./` to the root of the origin, here `https://docs.jmrp.io/`, the address the host's
own hub already claims with a manifest of its own, and an `id` that stays inside this site has to
spell out `/mikroscope/`, the base path nothing else in the manifest names. Left out, it defaults to
`start_url`, which Chromium resolves to `/mikroscope/`, the `id` it recommends.

One `theme-color` tag gives the colour of the header on screen, `#151c20` dark and `#fafbfb` light.
It follows the site's theme, not the system's scheme: it is served with the dark header, the theme a
page without JavaScript keeps whatever the system says, and a script in the head moves it to the
light one whenever the page turns light, from the system's scheme, the theme select, a stored choice
or the phone's toggle. Chrome reads the tag; Safari 26, by public reports and not tried here,
ignores it and takes the colour of the fixed header. In headless Chromium and WebKit, in both
schemes, after a pick, after a stored pick and a reload, after the phone's toggle and with
JavaScript off, the tag held the header's colour every time; the pair of tags split by
`prefers-color-scheme` it replaced held the other theme's colour after each pick, and on a light
system without JavaScript. Whether a phone's Chrome repaints its toolbar the moment the tag changes
was not tried on a device.

`TestTheCommittedWebFilesMatchTheGenerator` compares `favicon.svg` and `site.webmanifest` in
`site/public/` with what the generator writes now, so an edit that is not regenerated fails
`make test`; the PNGs depend on the installed converters and are not compared. Not tested: a real
phone, an install to a home screen, and how a launcher actually crops the maskable icon.

The mark in this site's header is `favicon-inline.svg`, the five-bar drawing, painted by the
site's own palette; `mark-inline.svg` fills the hero slot. Either way the drawing in the chrome is
the drawing in `brand/`.

## Background

Not written by the generator: `background.png` was generated once with
inference.sh (`openai/gpt-image-2`, 1536×1024, `quality: high`, $0.16) and is
kept as a raster. Everything drawn over it is vector, so the type stays crisp
at whatever size the raster is produced. The prompt asked for a near-black
field of faint vertical sample bars growing denser and warmer toward the right,
and for the left third to stay empty, which is where the mark and the type
sit, so the composition never fights its own background.

The three crops keep the whole left-to-right gradient rather than taking a
window out of the middle of it:

```sh
magick background.png -resize 1280x -gravity center -crop 1280x320+0+0 +repage bg-banner.png
magick background.png -resize 1280x -gravity center -crop 1280x640+0+0 +repage bg-social.png
magick background.png -resize 1200x -gravity center -crop 1200x630+0+0 +repage bg-og.png
```

The compositions are light on dark throughout, so they take the dark theme's
two tones and need no theme pair. Measured over the field's own darkest ground
(`#020608`, sampled from the left edge), the heading `#f6f3ee` reads 18.38:1,
the tagline `#cfc6b8` 12.04:1, and the mark's two tones 12.19:1 and 5.62:1:
higher than on the page, the field being darker than it. The tagline is "Sub-second
kernel telemetry from inside the router".

## Social preview

Manual: Settings, then Social preview, then upload `social.png`. GitHub offers
no API for it.

## See also

- [Dashboards](https://docs.jmrp.io/mikroscope/dashboards/): where amber means a threshold.
- [The landing page](https://docs.jmrp.io/mikroscope/): the claim the mark draws, in words.
- [Lineage and licence](https://docs.jmrp.io/mikroscope/about/lineage/): where the rest of the code came from.
