Skip to content
Brok UI

Loading…

No results

Transport Badge

Open source

A transit station badge for Singapore MRT/LRT and Hong Kong MTR with semantic theme colours by default and optional official line colours.

Version
v1.1.3
Stability
stable
License
MIT
Related
Badge
Release Badge
CI Badge

Preview

Jurong East Bugis Tsim Sha Tsui
Size
previews.components.transport-badge.default.blade.php Blade
<div class="flex flex-col gap-3">
    <x-ui.transport-badge system="SG" station-code="NS1" :station-name="__('Jurong East')" :show-station-name="true" />
    <x-ui.transport-badge system="SG" station-code="DT15" :station-name="__('Bugis')" :show-station-name="true" />
    <x-ui.transport-badge system="HK" station-code="TW3" :station-name="__('Tsim Sha Tsui')" :show-station-name="true" />
</div>
Xs Current
Sm Current
Md Current
Lg Current

Installation

terminal
php artisan ui:add transport-badge

Registry contract

php artisan ui:add transport-badge writes only the files below. The CLI validates each file hash before writing and asks before it replaces a local change, unless you pass --force.

  • blade resources/views/components/ui/transport-badge.blade.php
  • css resources/css/themes/transport-badge.css
Registry dependencies
None — installs on its own.
Packages
composer: jml/brok:^0.2

Use with AI

A brief for your coding agent: install command, usage, props, guidance and the rules. Copy it, or open a prompt about this component in an assistant.

transport-badge.md
# Brok UI: Transport Badge (`transport-badge`)

A transit station badge for Singapore MRT/LRT and Hong Kong MTR with semantic theme colours by default and optional official line colours.

Brok UI is a Laravel Blade component registry. Installed components are plain Blade files the app owns.

## Install

```bash
php artisan ui:add transport-badge
```

## Usage

```blade
<div class="flex flex-col gap-3">
    <x-ui.transport-badge system="SG" station-code="NS1" :station-name="__('Jurong East')" :show-station-name="true" />
    <x-ui.transport-badge system="SG" station-code="DT15" :station-name="__('Bugis')" :show-station-name="true" />
    <x-ui.transport-badge system="HK" station-code="TW3" :station-name="__('Tsim Sha Tsui')" :show-station-name="true" />
</div>
```

## Props

- `system` (string, default `SG`) — Transit system whose line colors and label apply, such as SG (Singapore) or HK (Hong Kong).
- `stationCode` (mixed|null, default `null`) — One line code, or an array of codes for an interchange station.
- `stationName` (mixed|null, default `null`) — Station name used in the accessible label and shown when showStationName is true.
- `showStationName` (bool, default `false`) — Shows the station name as visible text next to the code chips.
- `size` (md|xs|sm|lg, default `md`) — Chip size: xs, sm, md, or lg.
- `colorMode` (string, default `semantic`) — semantic uses theme tokens for every chip; brand uses each line's official transit color.

## Use when

- Use for compact labels, counts, and concise metadata that support scanning.
- Showing a transit line or station code with its official identity, such as in a directions list or station card.
- Displaying several line codes for an interchange station together as one badge.

## Avoid when

- Do not force badges or short labels to carry complex explanations that need sentence-level context.
- The value is a generic status or count rather than a transit line; use badge instead.

## Anti-patterns

- Encoding complex meaning in a badge alone

## Rules

- Use the `<brok:transport-badge>` tag (or `<x-ui.transport-badge>`) in Blade; do not rewrite the component.
- Prefer the documented props and variants over utility-class overrides; when a utility must win, use the `!` important modifier.
- Use semantic design tokens (`bg-primary`, `text-muted-foreground`), never raw colour utilities.
- Keep the `data-slot` attributes; they are the styling and test hooks.

## Links

- Docs: https://brokui.dev/docs/components/transport-badge
- Registry JSON (files, props, contract): https://brokui.dev/r/open/transport-badge.json

Working in Claude Code, Cursor or Codex? Give the agent the whole registry through the MCP server or the Brok UI skill.

Examples

interchange.blade.php Blade
<div class="flex flex-wrap items-center gap-4">
    <x-ui.transport-badge system="SG" :station-code="['NS26', 'EW14']" :station-name="__('Raffles Place')" :show-station-name="true" />
    <x-ui.transport-badge system="SG" :station-code="['NS24', 'NE6', 'CC1']" :station-name="__('Dhoby Ghaut')" :show-station-name="true" size="sm" />
</div>

API

manifest knowledge + registry-derived coverage

Props

Props accepted by this component: name, type, default value and description.
Prop Type Default Description
system string SG Transit system whose line colors and label apply, such as SG (Singapore) or HK (Hong Kong).
stationCode mixed | null null One line code, or an array of codes for an interchange station.
stationName mixed | null null Station name used in the accessible label and shown when showStationName is true.
showStationName bool false Shows the station name as visible text next to the code chips.
size md | xs | sm | lg md Chip size: xs, sm, md, or lg.
colorMode string semantic semantic uses theme tokens for every chip; brand uses each line's official transit color.

Slots

  • default —

Data slots

Stable hooks for CSS overrides and browser tests.

transport-badge

Behavior

  • colorMode selects semantic, theme-driven color, or brand, each line's official transit color, for every code's chip.
  • Each code's color is resolved from its leading letters against the system's line map, falling back to a neutral chip when the code is unknown.
  • Declares registry capability flags: a11y, responsive, rtl, darkMode, localized.

Guidance

Compact label and metadata

Communicate concise status, count, or metadata.

Use when

  • Use for compact labels, counts, and concise metadata that support scanning.
  • Showing a transit line or station code with its official identity, such as in a directions list or station card.
  • Displaying several line codes for an interchange station together as one badge.

Avoid when

  • Do not force badges or short labels to carry complex explanations that need sentence-level context.
  • The value is a generic status or count rather than a transit line; use badge instead.

Use instead

  • Visible explanatory text

Anti-patterns

  • Encoding complex meaning in a badge alone
Anatomy
root
Theming hooks
transport-badge

Accessibility

WCAG 2.2 AA Keyboard focus-visible RTL-ready Localized labels Dark mode
Keyboard
No component-owned keyboard interaction; native element behavior applies.
Focus
none
  • The root carries role=img and an aria-label combining the system name with the station name or codes, since the chips themselves are aria-hidden.
  • Semantic HTML and a stable data-slot attribute for styling and scripting hooks.
  • Focus-visible rings use the ring token, so keyboard focus is always visible.
  • Disabled and invalid states are conveyed to assistive tech, not by color alone.
  • Targets WCAG 2.2 AA; verify contrast in light, dark, admin and customer surfaces in the preview.
  • Labels go through __() and layout uses logical properties (ms-*, text-start), so it mirrors under dir="rtl" — flip the preview to RTL to confirm.
  • Dark mode uses the same semantic tokens under the dark class; high contrast follows forced-color system tokens.

Livewire

Safe

Livewire can update this component through forwarded wire:* attributes.

Source

The exact, editable files ui:add writes into your app. Previews render this same code; there are no preview-only components.

resources/views/components/ui/transport-badge.blade.php Blade
@props([
    'system' => 'SG',
    'stationCode' => null,
    'stationName' => null,
    'showStationName' => false,
    'size' => 'md',
    'colorMode' => 'semantic',
])

@php
    // Official transit line colours are available as an explicit brand mode.
    // Semantic mode remains the default so global theme changes affect the badge.
    $systems = [
        'SG' => [
            'label' => 'Singapore MRT/LRT',
            'lines' => [
                'NS' => 'bg-transport-sg-ns', 'EW' => 'bg-transport-sg-ew', 'CG' => 'bg-transport-sg-ew',
                'NE' => 'bg-transport-sg-ne', 'CC' => 'bg-transport-sg-cc', 'CE' => 'bg-transport-sg-cc',
                'DT' => 'bg-transport-sg-dt', 'TE' => 'bg-transport-sg-te',
                'BP' => 'bg-transport-sg-lrt', 'SK' => 'bg-transport-sg-lrt', 'PG' => 'bg-transport-sg-lrt',
            ],
        ],
        'HK' => [
            'label' => 'Hong Kong MTR',
            'lines' => [
                'TW' => 'bg-transport-hk-tw', 'KT' => 'bg-transport-hk-kt', 'IS' => 'bg-transport-hk-is',
                'TK' => 'bg-transport-hk-tk', 'TC' => 'bg-transport-hk-tc', 'AE' => 'bg-transport-hk-ae',
                'TM' => 'bg-transport-hk-tm', 'EA' => 'bg-transport-hk-ea', 'SI' => 'bg-transport-hk-si',
                'DR' => 'bg-transport-hk-dr',
            ],
        ],
    ];

    $sizes = [
        'xs' => 'h-5 min-w-5 px-1 text-[0.625rem]',
        'sm' => 'h-6 min-w-6 px-2 text-xs',
        'md' => 'h-7 min-w-7 px-2 text-sm',
        'lg' => 'h-9 min-w-9 px-2 text-base',
    ];

    $sys = $systems[$system] ?? $systems['SG'];
    $fallback = 'bg-transport-fallback';
    $sizeCls = $sizes[$size] ?? $sizes['md'];
    $useBrandColour = $colorMode === 'brand';

    $codes = is_array($stationCode)
        ? $stationCode
        : ($stationCode !== null && $stationCode !== '' ? [$stationCode] : []);

    $lineClass = static function (string $code) use ($sys, $fallback): string {
        preg_match('/^[A-Za-z]+/', $code, $m);
        $key = strtoupper($m[0] ?? '');
        return $sys['lines'][$key] ?? $fallback;
    };

    $aria = $stationName ?: implode(' ', $codes);
@endphp

<span
    data-slot="transport-badge"
    data-system="{{ $system }}"
    data-color-mode="{{ $useBrandColour ? 'brand' : 'semantic' }}"
    role="img"
    aria-label="{{ trim($sys['label'].' '.$aria) }}"
    {{ $attributes->merge(['class' => 'inline-flex items-center gap-2 align-middle']) }}
>
    <span class="inline-flex items-center gap-1" aria-hidden="true">
        @foreach ($codes as $code)
            <span
                class="inline-flex items-center justify-center rounded-md font-semibold tabular-nums text-primary-foreground {{ $useBrandColour ? $lineClass($code) : 'bg-primary' }} {{ $sizeCls }}"
            >{{ $code }}</span>
        @endforeach
    </span>

    @if ($showStationName && $stationName)
        <span class="font-medium text-foreground">{{ $stationName }}</span>
    @endif
</span>
resources/css/themes/transport-badge.css CSS
/* Domain-owned transport palette. This file is installed only with transport-badge. */
@theme inline {
    --color-transport-sg-ns: hsl(var(--transport-sg-ns));
    --color-transport-sg-ew: hsl(var(--transport-sg-ew));
    --color-transport-sg-ne: hsl(var(--transport-sg-ne));
    --color-transport-sg-cc: hsl(var(--transport-sg-cc));
    --color-transport-sg-dt: hsl(var(--transport-sg-dt));
    --color-transport-sg-te: hsl(var(--transport-sg-te));
    --color-transport-sg-lrt: hsl(var(--transport-sg-lrt));
    --color-transport-hk-tw: hsl(var(--transport-hk-tw));
    --color-transport-hk-kt: hsl(var(--transport-hk-kt));
    --color-transport-hk-is: hsl(var(--transport-hk-is));
    --color-transport-hk-tk: hsl(var(--transport-hk-tk));
    --color-transport-hk-tc: hsl(var(--transport-hk-tc));
    --color-transport-hk-ae: hsl(var(--transport-hk-ae));
    --color-transport-hk-tm: hsl(var(--transport-hk-tm));
    --color-transport-hk-ea: hsl(var(--transport-hk-ea));
    --color-transport-hk-si: hsl(var(--transport-hk-si));
    --color-transport-hk-dr: hsl(var(--transport-hk-dr));
    --color-transport-fallback: hsl(var(--transport-fallback));
}

:root,
.dark {
    --transport-sg-ns: 0.3 76.4% 51.8%;
    --transport-sg-ew: 151.2 100% 29.4%;
    --transport-sg-ne: 293.1 39.7% 42.9%;
    --transport-sg-cc: 33.8 94.6% 56.3%;
    --transport-sg-dt: 206.4 100% 32.9%;
    --transport-sg-te: 26.7 61% 38.2%;
    --transport-sg-lrt: 123.2 7.8% 48%;
    --transport-hk-tw: 7.9 74.6% 47.8%;
    --transport-hk-kt: 128.9 47% 42.2%;
    --transport-hk-is: 203.8 100% 38%;
    --transport-hk-tk: 277.1 36.5% 45.1%;
    --transport-hk-tc: 27.9 92% 60.6%;
    --transport-hk-ae: 185.7 42.4% 38.8%;
    --transport-hk-tm: 20.2 82.4% 31.2%;
    --transport-hk-ea: 200.6 71.3% 63.1%;
    --transport-hk-si: 63.9 65.4% 46.5%;
    --transport-hk-dr: 330 76.7% 71.4%;
    --transport-fallback: 0 0% 35.7%;
}

Ownership & lifecycle

Owner, release state, review evidence and adoption for this item.
Owner
Platform UI (@JoshJML)
Current version
1.1.3
Status
Stable
License
open
Accessibility reviewed
No review date recorded
Last breaking change
No date recorded
Deprecation
Not deprecated
Contract
v6
Foundation
≥ 1.0.0