WebberUI

Taiwan Tile Map (React)

A React tile-grid map of Taiwan's 22 counties and cities that needs no geo data: value-driven choropleth colouring, hover tooltip cards, click callbacks and arrow-key navigation, usable as a visual filter.

This is a WebberUI Pro component

Free during the launch campaign: sign up or sign in, then hit “Copy install command” in the preview above and it installs straight away — no payment, no credit card. The command below returns 401 while you are signed out.

How to install Pro components →See the plans →

Like a US-state tile-grid map, this lays out Taiwan's outline with equal-sized rounded squares — the main island runs north to south, the outlying islands sit on the left, and a 5 × 11 grid is the whole picture, with no GeoJSON or projection library to load. Pass data (county code or name → number) and every tile is coloured by interpolating along the value range, while counties without data stay light grey. Hovering or focusing a tile lifts it and floats a card with the name and value; clicking adds an outline plus a check badge and fires a callback, which makes it a natural visual filter for store distribution, epidemic or sales statistics. The 22 codes, full names, short names and default grid positions ship as TAIWAN_REGIONS, and any position can be overridden through layout.

Loading preview…
npx shadcn@latest add "https://webberui.com/r/taiwan-tile-map.json?t=<install token>"

Playground

Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.

<TaiwanTileMap />

Installation

npx shadcn@latest add "https://webberui.com/r/taiwan-tile-map.json?t=<install token>"

Or, once registries are configured in components.json, install it as @webberui/taiwan-tile-map.

Usage

import { TaiwanTileMap, TAIWAN_REGIONS } from "@/components/ui/taiwan-tile-map";

// Keys can be codes (TPE) or county names (臺北市 / 台北市); counties not listed show as "no data"
const storeCounts = { 臺北市: 128, 新北市: 176, TXG: 104, KHH: 98, 澎湖縣: 3 };

<TaiwanTileMap
  data={storeCounts}
  labels="short"
  onSelect={(code, selected) => console.log(code, selected)}
/>

// Multi-select as a filter, with a fixed colour domain so months compare consistently
<TaiwanTileMap
  data={storeCounts}
  multi
  domain={[0, 200]}
  colors={["#e0f2fe", "#075985"]}
  labels="value"
  tooltipFormatter={(region, value) => `${region.name}: ${value ?? 0} stores`}
/>

// The built-in constant is handy for menus, lookup tables or custom positions
TAIWAN_REGIONS.map((r) => r.code); // ["LIE", "KIN", "PEN", "TPE", ...]

Props

PropTypeDefaultDescription
dataRecord<string, number>Value per county; keys may be codes (TPE) or county names (臺北市 / 台北市). Counties without data render light grey
colorScale(t: number) => stringCustom colour scale: takes the normalised value 0..1 and returns any CSS colour; takes precedence over colors
colors[string, string]["#d1fae5", "#047857"]End colours [lightest, darkest] as hex, interpolated linearly in RGB
domain[number, number]Fixed [min, max] for the colour scale; when omitted the data's min / max is used
labels"name" | "short" | "value" | "none""short"What to print inside each tile: full name, short name, the value, or nothing
selectedstring | string[] | nullControlled selection: a county code (or name); an array when multi, null for none
defaultSelectedstring | string[] | nullnullInitial selection in uncontrolled mode
onSelect(code: string, selected: string[]) => voidFired when a tile is clicked: code is the county just toggled, selected is the full selection after the toggle
multibooleanfalseAllow multiple selection (clicking a selected tile again deselects it)
showLegendbooleantrueShow the gradient legend below the map (min / max plus a "no data" swatch)
noDataLabelstring"無資料" (no data)Text shown in the tooltip and legend when a county has no value
tooltipFormatter(region: TaiwanRegion, value: number | undefined) => React.ReactNodeCustom tooltip content; the returned node replaces the default "name + value"
valueFormatter(value: number) => stringzh-TW thousands groupingNumber formatting used for value labels, the tooltip and the legend
layoutPartial<Record<string, readonly [number, number]>>Override individual grid positions: code → [col, row]; unlisted counties keep the defaults
size"sm" | "md" | "lg""md"Size preset: controls the map's max width, gap, corner radius and font size
aria-labelstring"台灣縣市方塊地圖" (Taiwan tile map)Accessible label of the outermost group
classNamestringForwarded to the outermost container

TaiwanRegion

Each entry of the TAIWAN_REGIONS constant, and the first argument of tooltipFormatter.

FieldTypeDefaultDescription
codestringCounty code (three-letter ISO 3166-2:TW style, e.g. TPE)
namestringOfficial full name, e.g. 臺北市 (Taipei City)
shortstringOne- or two-character abbreviation, e.g. 北 / 竹市
layoutreadonly [col: number, row: number]Default grid position; top-left is [0, 0], increasing right and down

TAIWAN_REGIONS (22 TaiwanRegion entries), resolveTaiwanRegion(key) (look up by code or county name, accepting both 臺 and 台) and the TaiwanTileLabel / TaiwanTileMapSize types are also named exports.

How it works

  • Default positions: Lienchiang [0,1], Kinmen [0,2], Penghu [0,7]; Taipei [3,0], Keelung [4,0]; Taoyuan [2,1], New Taipei [3,1], Yilan [4,1]; Hsinchu City [1,2], Hsinchu County [2,2]; Miaoli [2,3]; Taichung [2,4], Nantou [3,4], Hualien [4,4]; Changhua [1,5]; Yunlin [1,6]; Chiayi City [1,7], Chiayi County [2,7], Taitung [4,7]; Tainan [1,8]; Kaohsiung [1,9]; Pingtung [2,10]. The grid is CSS grid (repeat(5, minmax(0, 1fr)) with aspect-square); the column count is derived from the positions, so overriding layout re-flows the whole map.
  • Colouring: each value is normalised to 0..1 ((value − min) / (max − min), with the range taken from the data or domain) and then handed to colorScale, or interpolated in RGB between the two colors; a single value is treated as the darkest shade. Text inside a tile switches between dark and light based on the tile colour's WCAG relative luminance, falling back to the t value when a custom scale returns something unparseable (such as hsl()).
  • Tooltip: triggered by hover or keyboard focus, it opens downward on the top two rows and upward elsewhere, and aligns to the edge on the leftmost / rightmost column — no measuring, and it never leaves the map's bounds.
  • Entrance animation: tiles fade and scale in staggered by row, with a slight extra offset by col within a row, so the map appears to unfurl from north to south; when layout is overridden or size changes, tile positions glide with a layout animation.

Accessibility

  • The grid is a role="group" with an aria-label (customisable) and, when the legend is shown, an aria-describedby pointing at it; every county is a type="button" whose aria-label includes the full name and value (or the no-data text), with aria-pressed reflecting selection
  • Roving tabindex: the whole map is a single Tab stop; arrow keys move along the same column / row first and jump diagonally to the nearest tile when nothing lies in that direction, Home / End go to the first / last tile, Enter / Space toggle selection and Esc dismisses the tooltip; the focused tile gets a focus-visible ring
  • Selection changes are written to a hidden aria-live="polite" region (e.g. "已選取 臺北市、桃園市" — selected Taipei City, Taoyuan City), and selected tiles get a check badge so selection is not conveyed by colour alone; the tooltip and legend bar are aria-hidden
  • When the user has "reduce motion" enabled at the system level, the staggered entrance, hover lift and layout animations are disabled and the tooltip simply appears / disappears, while colour and outline feedback stay the same

On this page