- Design system
- Logo & identity
01 — Logo & identity
Logo & identity
One mark, shared by every tanghoong property. It is an arc and a circle — no letterforms, no font dependency, no theme variants.
The mark
A C and a · on a dark tile. The C is the initial; the dot is the separator this writing already uses everywhere — "Selangor, Malaysia · UTC+8", "Discovery · Integration". The mark is built from the punctuation of the prose rather than from a decorative flourish.
The 16 px rendering is the one that governs every decision below. If a change does not survive there, it does not ship.
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"
role="img" aria-label="Charlie Tang Hoong">
<rect width="64" height="64" rx="14.3" fill="#1d1d1f"/>
<path d="M37 22a13 13 0 1 0 0 20" fill="none"
stroke="#4dff9b" stroke-width="5.6" stroke-linecap="round"/>
<circle cx="49" cy="32" r="4.6" fill="#4dff9b"/>
</svg>
Three elements, 1.5 KB with its comments, and no external dependency. That is the whole identity.
Geometry
Everything is stated as a fraction of the 64-unit box, so the mark can be redrawn at any size without a reference file.
| Element | Value | Of the box | Note |
|---|---|---|---|
| Tile | 64 × 64 | 100% | The whole viewBox; the mark is never used without it |
| Corner radius | 14.3 | 22.3% | Close to the iOS icon ratio, so it sits right on a home screen |
| Arc radius | 13 | 20.3% | Centred at 24,32 — left of centre, to leave room for the dot |
| Arc stroke | 5.6 | 8.75% | Round cap. Heavy enough to hold at 16 px |
| Dot radius | 4.6 | 7.2% | At 49,32 — on the arc's horizontal axis |
| Optical gap | ≈7.4 | 11.6% | Arc terminal to dot edge. The gap is what reads as a space |
Colour
The mark is deliberately not theme-aware. One file, one pair of colours, on light and dark browser chrome alike. This is the single place in the system where a colour is hard-coded rather than tokenised — and it is on purpose.
| Pairing | Contrast | Verdict |
|---|---|---|
#4dff9b on #1d1d1f | 12.9 : 1 | Ships |
#03744e on #1d1d1f | 2.89 : 1 | Rejected the light accent is illegible on the tile |
#4dff9b on #ffffff | 1.30 : 1 | Rejected this is why the tile exists at all |
On white chrome
On black chrome
The tile is opaque, so it carries its own contrast wherever it lands. A transparent mark would need a light and a dark twin, and browsers do not reliably let a favicon switch on scheme.
<img>, an email signature. It has to carry its colours with it.
Size and minimums
The mark is legible down to 16 px because it was designed there first. Below that it stops being a C and becomes a smear — do not use it.
| Size | Where | File |
|---|---|---|
| 16 | Browser tab, bookmark bar — the minimum | favicon.ico |
| 28 | Site header, beside the wordmark | inline SVG |
| 32 / 48 | Retina tab, Windows shortcut | favicon.ico |
| 180 | iOS home screen | apple-touch-icon.png |
| 192 / 512 | Android, PWA install, share cards | icon-192.png, icon-512.png |
| any | Everything modern | favicon.svg |
Clear space
Keep a margin of at least the dot's diameter — 9.2 units, about 14% of the box — on every side. The tile's own corner radius reads as tight if anything crowds it.
Lockup
Mark plus wordmark, as it appears in the header. The wordmark is the system font at 600 with -0.02em tracking — the same treatment as an h3, so it sits with the rest of the type rather than beside it.
On a sub-domain, the qualifier changes and nothing else does:
<a class="brand" href="/">
<svg class="brand__mark" viewBox="0 0 64 64" aria-hidden="true" focusable="false">
<rect width="64" height="64" rx="14.3" fill="#1d1d1f"/>
<path d="M37 22a13 13 0 1 0 0 20" fill="none" stroke="#4dff9b"
stroke-width="5.6" stroke-linecap="round"/>
<circle cx="49" cy="32" r="4.6" fill="#4dff9b"/>
</svg>
<span>tanghoong <span class="t3">design</span></span>
</a>
The mark is aria-hidden and focusable="false" — the link's accessible name comes from the wordmark. Where the wordmark is visually hidden, keep it in the DOM with .vh: a link with no text has no name at all.
Misuse
1.3:1 on white it disappears.Files and link order
Five files. The order of the <link> tags matters more than it looks.
<!-- The .ico goes FIRST. Chrome, Firefox and Safari all prefer the SVG
regardless; Googlebot's favicon fetcher and older browsers want a raster,
and with no .ico declared they guess /favicon.ico, get a 404, and the
search result renders with a blank tile. sizes="any" is what stops a
modern browser choosing the .ico over the SVG. -->
<link rel="icon" href="/favicon.ico" sizes="any">
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
<link rel="manifest" href="/site.webmanifest">
| File | Contains | Size |
|---|---|---|
| favicon.svg | The master. Every other file is generated from it. | 1.5 KB |
| favicon.ico | 16, 32 and 48 px, for Googlebot and older browsers | 15 KB |
| apple-touch-icon.png | 180×180, iOS home screen | 12 KB |
| icon-192.png | 192×192, Android and PWA | 13 KB |
| icon-512.png | 512×512, splash and share cards | 52 KB |
| site.webmanifest | Names the icon set for install prompts | <1 KB |
# ImageMagick. Strip the leading comment block first — the rasteriser
# will not parse the file with it in place.
sed '/^<!--/,/^-->/d' favicon.svg > /tmp/mark.svg
magick -background none /tmp/mark.svg -resize 180x180 -strip apple-touch-icon.png
magick -background none /tmp/mark.svg -resize 192x192 -strip icon-192.png
magick -background none /tmp/mark.svg -resize 512x512 -strip icon-512.png
for s in 16 32 48; do
magick -background none /tmp/mark.svg -resize ${s}x${s} /tmp/i$s.png
done
magick /tmp/i16.png /tmp/i32.png /tmp/i48.png -strip favicon.ico
Never hand-edit a generated file. Change favicon.svg and re-run this — and then look at the 16 px output before committing.