4a3e12c85a
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Has been skipped
CI / integration (pull_request) Has been skipped
CI / ui (pull_request) Failing after 2m10s
CI / conformance (pull_request) Successful in 10s
CI / gate (pull_request) Failing after 0s
CI / deploy (pull_request) Has been skipped
The adaptive foreground's asterisk grazed the round mask's safe zone. Scale the foreground mark 0.75 about the icon centre (25% smaller, the letter-to-star arrangement unchanged) so it sits well inside the 61% safe zone. Independently, the legacy square ic_launcher.png now renders the full-bleed master instead of the safe-zone composite, so old Android (< API 26) shows a large mark rather than a shrunk one; the legacy round icon keeps the safe-zone composite (a round mask would clip the master's corner star). Regenerate the layer set, Android res and the brandbook foreground previews; update ICON_BRANDBOOK.md / ICONS.md.
168 lines
8.1 KiB
Markdown
168 lines
8.1 KiB
Markdown
# Erudit app-icon — brand book
|
||
|
||
The single source of truth for **how the Erudit icon is constructed**. It is written to
|
||
be reproducible *from words alone*: a designer with this document and the Spectral font
|
||
can rebuild the icon exactly, at any size, without pixel-matching anything.
|
||
|
||
Two rules make that possible:
|
||
|
||
1. **The icon is a square.** Every size and position below is a **fraction of the
|
||
square's side** (equivalently, a percentage). Nothing is in pixels. Draw the square
|
||
at whatever resolution you need and scale each value to it.
|
||
2. **The origin is the top-left corner.** `x` grows right, `y` grows down. So "center
|
||
`48.1% / 49.2%`" means a point `0.481·side` from the left and `0.492·side` from the top.
|
||
|
||
The companion generator in [`../assets/icons/brand/`](../assets/icons/brand/) is the
|
||
machine version of this exact spec (`build-icon.mjs`, where each constant is the same
|
||
fraction printed here). The `docs/ICONS.md` map covers the *opposite* concern — which
|
||
files/sizes we slice this master into for each platform.
|
||
|
||
## Anatomy
|
||
|
||

|
||
|
||
Bottom-to-top the icon is six layers: **tile → wood grain → light sheen → shadow →
|
||
letter «Э» → star ✻**. The letter and star are always drawn last, at full ink colour;
|
||
the grain and the two gradients only ever touch the background.
|
||
|
||
## Palette
|
||
|
||
The icon ships as a **light** and a **dark** variant (a matched pair — the light tile
|
||
carries dark ink, the dark tile carries light ink). Each variant uses four colours.
|
||
|
||
| Role | Light | Dark |
|
||
|------|-------|------|
|
||
| **Tile** — the wooden body | `#e6b053` | `#4a3316` |
|
||
| **Ink** — the «Э» and the ✻ | `#5a3a12` | `#f2d9a0` |
|
||
| **Grain** — the vertical stripes | `#d8a54e` | `#432e14` |
|
||
| **Sheen** — diagonal light | white, α 0 → 15 % | white, α 0 → 15 % |
|
||
| **Shadow** — diagonal dark | black, α 15 → 0 % | black, α 15 → 0 % |
|
||
|
||
The grain colour is intentionally close to the tile — a hair darker — so the stripes
|
||
read as wood texture, not as stripes.
|
||
|
||
## 1 — Tile
|
||
|
||
A **square with rounded corners**, corner radius **17 %** of the side, filled with the
|
||
**Tile** colour. This is the whole background; there is no separate outer frame or
|
||
border. Corners are baked at 17 %; platform masks (iOS/Android) may round further on top.
|
||
|
||
## 2 — Wood grain
|
||
|
||
Subtle vertical planks over the tile:
|
||
|
||
- Divide the width into **6 equal columns** (each `1/6` of the side wide).
|
||
- On the **right edge of each column**, draw one **vertical stripe**, width **`1/32` of
|
||
the side** (≈ 3.1 %), running the full height.
|
||
- Move the **whole set of six stripes left** by **`1/16` of the side** (= two stripe
|
||
widths).
|
||
- **Tilt every stripe 4°** (clockwise, about its own centre).
|
||
- Draw the stripes **past the top and bottom edges** so the tilt leaves no empty corners,
|
||
then **clip the whole set to the tile** (so the rounded corners stay clean).
|
||
- Colour: **Grain**.
|
||
|
||
## 3 — Light sheen · 4 — Shadow
|
||
|
||
Two linear gradients along the **same diagonal**, from the **bottom-left corner to the
|
||
top-right corner**, each clipped to the tile and sitting **above the grain, below the
|
||
letter**:
|
||
|
||
- **Sheen** — white. Fully transparent at the bottom-left, rising to **15 % opacity** at
|
||
the top-right. Brightens the top-right.
|
||
- **Shadow** — black. **15 % opacity** at the bottom-left, fading to transparent at the
|
||
top-right. Deepens the bottom-left.
|
||
|
||
Together they give the tile a lit-from-the-top-right, resting-in-shadow-at-the-bottom-left
|
||
sense of depth. Both percentages are a tuning knob (`--sheen`, `--shade`).
|
||
|
||
## 5 — The letter «Э»
|
||
|
||
- Typeface: **Spectral, Bold** (SIL OFL; pinned in the brand folder).
|
||
- Glyph: the Cyrillic capital **«Э»** (U+042D).
|
||
- Size: scale the glyph so its **ink bounding box is 60.5 % of the side tall** (its width
|
||
then follows the font — about 53.2 %).
|
||
- Position: place the **center of that bounding box** at **48.1 % / 49.2 %** (just up and
|
||
left of the icon center).
|
||
- Colour: **Ink**.
|
||
|
||
## 6 — The star ✻
|
||
|
||
A **six-spoke teardrop asterisk** (the "score" mark that replaces a tile's point number):
|
||
|
||
- Each spoke tapers to a **point at the center** and ends in a **round bulb**; a central
|
||
disc equal to a bulb fuses the six spokes.
|
||
- Outer diameter: **17.3 % of the side**.
|
||
- Bulb radius (and the central disc): **22 % of the star's radius**.
|
||
- Position: center at **78.8 % / 78.8 %** — tucked to the lower-right of the «Э», like a
|
||
subscript, with a clear gap from the letter.
|
||
- Colour: **Ink**.
|
||
|
||
Spectral has no ✻ glyph, so the star is **drawn geometrically**, not set as type. The exact
|
||
construction is in `build-icon.mjs` (`starPath`).
|
||
|
||
## Composition intent
|
||
|
||
The «Э» and the ✻ are treated as **one group**. Its combined bounding box measures
|
||
about **65.9 % × 68.6 %** of the side, and it is placed so its **bottom-right corner sits
|
||
at 87.5 % / 87.5 %** of the icon (the lower-right corner of an inner `12.5 % … 87.5 %`
|
||
safe square). That is what pushes the composition down-and-right and leaves the star room
|
||
in the corner. When re-deriving positions, this is the anchor to preserve.
|
||
|
||
> This bottom-right bias suits a **full-bleed** icon (iOS / PWA / favicon, which show
|
||
> almost the whole square). Android crops more, so it uses a **separate two-layer build**
|
||
> — see the next section. The master itself is never moved for Android.
|
||
|
||
## Android adaptive icon (two layers)
|
||
|
||
Android icons are **108×108 dp** and the OS applies its own mask (circle, squircle,
|
||
rounded square…). Only the **inner 66 dp = 61 % of the side, centred** is guaranteed
|
||
visible under every mask; the outer ~1/6 ring is used for the mask and motion effects.
|
||
So Android is **not** the full-bleed master — it is built as two layers:
|
||
|
||
- **Background** — the tile with wood grain and the light/shadow gradients, **full-bleed
|
||
and square** (no rounded corners; the OS supplies the shape). It fills the whole 108 dp
|
||
layer, so the sacrificial outer ring is just more wood.
|
||
- **Foreground** — **only the «Э» + ✻**, transparent, **re-placed** to sit safely inside
|
||
the mask. The whole mark is the master's optical placement (nudged right 8 % / down 3 %)
|
||
scaled **0.75 about the icon centre** — **25 % smaller**, its «Э» ↔ ✻ arrangement
|
||
unchanged — so the ✻ that used to graze the round mask now sits well inside the **61 %
|
||
safe zone** with margin to spare:
|
||
- «Э»: box height **40.4 %**, box center **51.8 % / 49.6 %**.
|
||
- ✻: outer diameter **9.9 %** (smaller than the master's), center **69.7 % / 67.0 %**,
|
||
tucked closer to the letter.
|
||
|
||
Everything else (palette, grain, gradients, star construction) is identical to the master.
|
||
|
||
## Variants in use
|
||
|
||
The **light** variant is the primary icon (launcher, favicon, apple-touch, PWA). The
|
||
**dark** variant is shipped in addition wherever the platform can pick by appearance
|
||
(e.g. an SVG favicon via `prefers-color-scheme`). Where a platform allows only one icon,
|
||
use **light**.
|
||
|
||
## Reproduce
|
||
|
||
```sh
|
||
cd assets/icons/brand
|
||
npm i opentype.js
|
||
# full master (iOS / PWA / favicon), either variant:
|
||
node build-icon.mjs --variant light --layer full > icon-light.svg
|
||
# Android adaptive layers:
|
||
node build-icon.mjs --variant light --layer background > icon-light-android-background.svg
|
||
node build-icon.mjs --variant light --layer foreground > icon-light-android-foreground.svg
|
||
# the construction figure:
|
||
node build-icon.mjs --variant light --scheme > icon-construction.svg
|
||
node render-png.mjs icon-light.svg icon-light.png 1024 # any size
|
||
```
|
||
|
||
`--layer` is `full` (default), `background`, or `foreground`. All outputs are committed
|
||
alongside the generator in [`../assets/icons/brand/`](../assets/icons/brand/).
|
||
|
||
## Masters
|
||
|
||
| | Light | Dark |
|
||
|-|-------|------|
|
||
| **Full** (iOS / PWA / favicon) |  |  |
|
||
| **Android background** |  |  |
|
||
| **Android foreground** |  |  |
|