Files
goodgo-platform/docs/design-system/DESIGN_SYSTEM_QUICK_REFERENCE.md
Ho Ngoc Hai 08b96f9c2d docs: consolidate exploration & audit reports under docs/ (TEC-3094)
- Move 8 stray .md (+5 .txt) from ~/Desktop into docs/explorations/from-desktop/
- Reorganize 27 .md/.txt at workspace root:
  - audit reports -> docs/audits/
  - exploration reports -> docs/explorations/
  - design system -> docs/design-system/
- Keep only README/CHANGELOG/CONTRIBUTING/CLAUDE at repo root
- Refresh docs/README.md as canonical index with links to all groups
- Note: pre-existing docs/audits/AUDIT_INDEX.md and AUDIT_SUMMARY.md were
  overwritten by the newer root-level versions during the move

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-21 16:29:24 +07:00

5.6 KiB

Design System Quick Reference

Component Imports

import {
  StatCard,
  PriceDelta,
  MarketIndex,
  DataTable,
  CompactHeader,
  DashboardLayout,
  TickerStrip,
} from '@/components/design-system';

Quick Examples

StatCard - KPI Display

<StatCard
  label="Giá TB/m²"
  value="45.2"
  unit="tr/m²"
  delta={+2.5}
  sublabel="24h"
  icon={<TrendingUp />}
/>

PriceDelta - Change Indicator

<PriceDelta value={5.2} size="md" />           {/* +5.20% ↑ Green */}
<PriceDelta value={-2.1} size="sm" />          {/* -2.10% ↓ Red */}
<PriceDelta value={0} direction="neutral" />   {/* 0.00% - Yellow */}

MarketIndex - Hero Metric

<MarketIndex
  name="GGX Market"
  value="1,240"
  changePercent={3.5}
  change={"+35"} 
  window="24h"
/>

DataTable - Sortable Data

const columns: DataTableColumn<District>[] = [
  {
    id: 'name',
    header: 'District',
    cell: (row) => row.name,
    sortable: true,
    sortValue: (row) => row.name,
  },
  {
    id: 'price',
    header: 'Price/m²',
    cell: (row) => `${row.avgPrice} tr`,
    align: 'right',
    numeric: true,
    sortable: true,
    sortValue: (row) => row.avgPrice,
  },
];

<DataTable columns={columns} data={districts} dense defaultSortId="price" />

TickerStrip - Scrolling Ticker

<TickerStrip
  items={[
    { id: 'q1', label: 'Quan 1', changePercent: 2.1 },
    { id: 'q2', label: 'Quan 2', changePercent: -1.3 },
  ]}
/>

DashboardLayout - Full Page Frame

<DashboardLayout
  header={<CompactHeader breadcrumb="Market" />}
  sidebar={<Navigation />}
  ticker={<TickerStrip items={items} />}
  sidebarCollapsed={collapsed}
>
  <div className="space-y-6">
    {/* Main content */}
  </div>
</DashboardLayout>

Design Tokens

Colors (CSS Variables)

Light Mode (:root)

  • --foreground: 220 20% 12% — Dark navy text
  • --background: 0 0% 97% — Off-white background
  • --background-elevated: 0 0% 100% — Pure white cards
  • --primary: 142 72% 42% — Green (up)
  • --signal-down: 0 84% 55% — Red (down)
  • --signal-neutral: 45 93% 45% — Yellow (neutral)

Dark Mode (.dark)

  • --foreground: 210 20% 90% — Off-white text
  • --background: 220 20% 4% — Very dark background
  • Same primary/signal colors (brightness-adjusted)

Typography

/* Font families */
font-sans  /* Inter */
font-mono  /* JetBrains Mono (tabular-nums) */

/* Data-specific sizes */
text-ticker   /* 0.8125rem — ticker animation */
text-data-sm  /* 0.75rem — compact stats */
text-data-md  /* 0.875rem — default metric */
text-data-lg  /* 1.25rem — large KPI values */

Spacing

h-row           /* 2.25rem (36px) — table row height */
h-ticker-bar    /* 2rem (32px) — ticker height */
h-header-compact /* 3rem (48px) — dashboard header */

Utilities

[data-numeric]  /* Applies font-variant-numeric: tabular-nums */
elevation-1     /* Subtle shadow: 0 1px 2px rgba(0,0,0,0.3) */
elevation-2     /* Prominent shadow: 0 4px 12px rgba(0,0,0,0.4) */

Analytics API

import { analyticsApi } from '@/lib/analytics-api';
import {
  useMarketReport,
  useHeatmap,
  usePriceTrend,
  useDistrictStats,
} from '@/lib/hooks/use-analytics';

// Raw API calls
const report = await analyticsApi.getMarketReport('Ho Chi Minh', 'month');
const heatmap = await analyticsApi.getHeatmap('Ho Chi Minh', 'month');

// React Query hooks
const { data: districts } = useDistrictStats('Ho Chi Minh', 'month');
const { data: trend } = usePriceTrend('Quan 1', 'Ho Chi Minh', 'apartment', ['month-1', 'month']);

Charts (Recharts)

import { PriceTrendChart } from '@/components/charts/price-trend-chart';
import { DistrictBarChart } from '@/components/charts/district-bar-chart';
import { AgentPerformance } from '@/components/charts/agent-performance';

// Line chart with dual Y-axis
<PriceTrendChart 
  data={[{ period: 'Jan', 'Gia/m2': 45, 'Tin đăng': 120 }]}
  height={350}
/>

// Bar chart
<DistrictBarChart 
  data={[{ district: 'Q1', price: 45, listings: 50 }]}
  dataKey="price"
/>

// Mixed dashboard (with mock data)
<AgentPerformance />

Maps (Mapbox)

import { DistrictHeatmap } from '@/components/charts/district-heatmap';
import { ListingMap } from '@/components/map/listing-map';
import { LocationPicker } from '@/components/map/location-picker';

// Heatmap
<DistrictHeatmap
  data={[
    { district: 'Quan 1', avgPriceM2: 150000, totalListings: 25, medianPrice: '7 tỷ' },
  ]}
  city="Ho Chi Minh"
  onDistrictClick={(name) => console.log(name)}
/>

// Listing markers
<ListingMap
  listings={listings}
  selectedListingId={selected?.id}
  onMarkerClick={(listing) => navigate(`/listing/${listing.id}`)}
/>

// Interactive location picker
<LocationPicker
  lat={currentLat}
  lng={currentLng}
  onChange={({ lat, lng }, resolved) => {
    // resolved.city, resolved.district, resolved.ward available
    setCoords({ lat, lng });
  }}
  height="400px"
/>

Best Practices

  1. Always use [data-numeric] or font-mono for numbers — ensures proper alignment
  2. Signal colors: Green (up), Red (down), Yellow (neutral)
  3. Spacing: Use Tailwind gap classes; gap-4 for cards, gap-6 for sections
  4. Sticky headers: sticky top-0 z-10 pattern
  5. Responsive: Mobile-first; md: for tablet breakpoints
  6. Dark mode: CSS variables handle both modes automatically
  7. No hardcoded colors: Use hsl(var(--primary)) pattern for dynamic theming

Environment Setup

# Required for maps
NEXT_PUBLIC_MAPBOX_TOKEN=your_token_here

# Optional
NEXT_PUBLIC_ANALYTICS_ENDPOINT=...