---
name: features-data-presentation
description: Preferred rendering patterns for file paths, file icons, relative time, date, numbers, badges, buttons, and icons.
---
# Data Presentation
## Preference Matrix
| Data | Preferred rendering | Class recipe |
|------|----------------------|-------------|
| File path | Mono, segmented emphasis, full value in tooltip | `font-mono truncate` + `title` + segment opacity (`op25/op50/op60`) |
| File icon | Name-first then extension mapping, stable literal classes | literal `i-*` class + semantic tint (`color-blue-500`, `op-fade`) |
| Time ago | Compact relative time in dense rows | `text-micro font-mono tabular-nums op-fade` |
| Date/time | Absolute date in tooltip/detail | `title`/tooltip + `toLocaleString()` |
| Number | Locale-formatted + tabular mono alignment | `text-micro font-mono tabular-nums` |
| Badge | Small semantic chip with low-alpha background | `inline-flex items-center gap-1 px-1.5 py-px rounded border border-emerald-500/40 bg-emerald-500/10 text-micro uppercase tracking-wide` |
| Button | Reuse semantic action shortcuts | `btn-action`, `btn-action-sm`, `btn-action-icon` |
| Icon | Icon + label pair for non-trivial actions | `inline-flex items-center gap-1` |
Use this matrix as default output behavior unless the host project already defines alternatives.
## File Path
- Use `font-mono` for technical identifiers and paths.
- Truncate in layout, but keep full value in `title`.
- Reduce separator and infrastructure segment emphasis (`./`, `/`, `node_modules`, `.pnpm`).
```html
./
node_modules/
.pnpm/
react
/index.js
```
## File Icon
- Map filename first (`dockerfile`, `.env`, `.gitignore`), then extension.
- Keep icon classes literal so UnoCSS can statically extract them.
- Return both icon and tint classes.
```ts
// @unocss-include
const byName = {
'dockerfile': { icon: 'i-ph-file-cloud-duotone', color: 'color-sky-500' },
'.env': { icon: 'i-ph-file-lock-duotone', color: 'color-amber-600 dark:color-amber-400' },
}
const byExt = {
ts: { icon: 'i-ph-file-ts-duotone', color: 'color-blue-500' },
js: { icon: 'i-ph-file-js-duotone', color: 'color-yellow-500' },
vue: { icon: 'i-ph-file-vue-duotone', color: 'color-emerald-500' },
md: { icon: 'i-ph-file-md-duotone', color: 'op-fade' },
}
export function fileIcon(path: string) {
const base = (path.split('/').pop() || path).toLowerCase()
const byFilename = byName[base as keyof typeof byName]
if (byFilename)
return byFilename
const ext = base.includes('.') ? base.slice(base.lastIndexOf('.') + 1) : ''
return byExt[ext as keyof typeof byExt] || { icon: 'i-ph-file-duotone', color: 'op-fade' }
}
```
## Time and Date
- Show compact relative age in dense lists (`45s`, `8m`, `3h`, `12d`, `2mo`, `1y`).
- Show absolute date/time on hover or detail rows.
- Use `tabular-nums font-mono` for changing numbers to avoid layout jitter.
```ts
export function formatAge(ts: number): string {
const s = Math.max(0, Math.floor((Date.now() - ts) / 1000))
if (s < 60) return `${s}s`
const m = Math.floor(s / 60)
if (m < 60) return `${m}m`
const h = Math.floor(m / 60)
if (h < 24) return `${h}h`
const d = Math.floor(h / 24)
if (d < 30) return `${d}d`
const mo = Math.floor(d / 30)
if (mo < 12) return `${mo}mo`
return `${Math.floor(d / 365)}y`
}
export const formatDateTime = (ts: number) => new Date(ts).toLocaleString()
```
```html
```
## Number
- Use locale-aware formatting for counts and percentages.
- Use explicit currency formatting for money.
- Render dense metrics as mono + tabular.
```ts
const countLabel = count.toLocaleString()
const percentLabel = ratio.toLocaleString(undefined, {
style: 'percent',
minimumFractionDigits: 1,
maximumFractionDigits: 1,
})
const costLabel = new Intl.NumberFormat(undefined, {
style: 'currency',
currency: 'USD',
}).format(cost)
```
```html
12,480
```
## Badges, Buttons, Icons
- Status badges: small, uppercase or mono, tinted border and text, low-alpha background.
- Buttons: unify around `btn-action*` shortcuts.
- Icons: pair icon + text, keep icon size near text size (`text-sm` or `text-[0.9em]`).
### Icon System Strategy
- Use one main icon family for controls and actions (for consistent stroke/weight).
- Use specialized file-type icon sets for path/file lists.
- Keep file icons tinted by category, but keep control icons mostly semantic (`color-active`, `op-fade`, status colors).
```ts
// Controls/actions
const controls = {
refresh: 'i-ph-arrow-clockwise-duotone',
close: 'i-ph-x',
openExternal: 'i-octicon-link-external-16',
}
// File types
const fileTypes = {
ts: 'i-ph-file-ts-duotone color-blue-500',
js: 'i-ph-file-js-duotone color-yellow-500',
md: 'i-ph-file-md-duotone op-fade',
}
```
```html
open
```
## Centralize Status Glyph Mapping
- Keep one source of truth for status icon, color, and label.
- Return literal class strings from mapping functions so UnoCSS can extract dynamic icon classes.
- Reuse mapping across list rows, headers, tabs, and tooltips.
```ts
// @unocss-include
type SessionStatus = 'thinking' | 'tool' | 'task' | 'questions' | 'error' | 'idle'
interface StatusGlyph {
icon: string
color: string
label: string
}
export function sessionStatusGlyph(status: SessionStatus): StatusGlyph {
switch (status) {
case 'thinking':
return { icon: 'i-ph-spinner-duotone animate-spin', color: 'color-amber-500', label: 'thinking' }
case 'tool':
return { icon: 'i-ph-gear-duotone animate-spin', color: 'color-orange-500', label: 'running a tool' }
case 'error':
return { icon: 'i-ph-warning-circle-duotone', color: 'color-red-500', label: 'error' }
default:
return { icon: 'i-ph-check-circle-duotone', color: 'color-emerald-500/70', label: 'idle' }
}
}
```