tanghoong design
  1. Design system
  2. 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 mark at every size it ships at
The mark at 16 pixels.
16
The mark at 20 pixels.
20
The mark at 28 pixels, the header size.
28 · header
The mark at 44 pixels.
44
The mark at 64 pixels.
64
The mark at 120 pixels.
120

The 16 px rendering is the one that governs every decision below. If a change does not survive there, it does not ship.

Source — the whole mark
<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.

Construction
arc centre 24,32 · r13 tile 64 · rx 14.3 (22.3%) dot 49,32 r4.6
ElementValueOf the boxNote
Tile64 × 64100%The whole viewBox; the mark is never used without it
Corner radius14.322.3%Close to the iOS icon ratio, so it sits right on a home screen
Arc radius1320.3%Centred at 24,32 — left of centre, to leave room for the dot
Arc stroke5.68.75%Round cap. Heavy enough to hold at 16 px
Dot radius4.67.2%At 49,32 — on the arc's horizontal axis
Optical gap≈7.411.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.

#1d1d1f

Tile. The light theme's --text, used here as a fixed value.

#4dff9b

Ink. The accent in its vivid form — the dark theme's --accent.

Why the tile, and why the vivid green — measured, not assumed
PairingContrastVerdict
#4dff9b on #1d1d1f12.9 : 1Ships
#03744e on #1d1d1f2.89 : 1Rejected the light accent is illegible on the tile
#4dff9b on #ffffff1.30 : 1Rejected this is why the tile exists at all
The mark on a white browser chrome.

On white chrome

The mark on a black browser 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.

This is the exception that proves the token rule Everywhere else, a literal hex in a component is a bug. The mark is a fixed asset reused across domains and embedded in files that have no access to the page's custom properties — a manifest, an <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.

SizeWhereFile
16Browser tab, bookmark bar — the minimumfavicon.ico
28Site header, beside the wordmarkinline SVG
32 / 48Retina tab, Windows shortcutfavicon.ico
180iOS home screenapple-touch-icon.png
192 / 512Android, PWA install, share cardsicon-192.png, icon-512.png
anyEverything modernfavicon.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.

Clear space
9.2 units minimum

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.

Header lockup
tanghoong design

On a sub-domain, the qualifier changes and nothing else does:

tanghoong cv tanghoong blog
<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

Don't Recolour it. The pair is fixed. A green tile with dark ink is not a variant, it is a different mark.
Don't Drop the tile and place the arc on the page background. At 1.3:1 on white it disappears.
Don't Rebuild the C from a font. It was geometry precisely so it would never depend on one.
Don't Rotate, skew, add a shadow, or animate it in the header.
Don't Use it below 16 px, or stretch it to a non-square box.
Do Scale it freely above 16 px — it is vector, and every value is a fraction of the box.

Files and link order

Five files. The order of the <link> tags matters more than it looks.

In every <head>
<!-- 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">
FileContainsSize
favicon.svgThe master. Every other file is generated from it.1.5 KB
favicon.ico16, 32 and 48 px, for Googlebot and older browsers15 KB
apple-touch-icon.png180×180, iOS home screen12 KB
icon-192.png192×192, Android and PWA13 KB
icon-512.png512×512, splash and share cards52 KB
site.webmanifestNames the icon set for install prompts<1 KB
Regenerating the raster set from the SVG
# 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.