Skip to main content

Component Patterns

This page documents the standard component patterns used across HostMetrics. All components follow the project’s muted, elegant aesthetic using exact hex values — never generic Tailwind color classes.

Cards / Widgets

The standard card is the most common container in the app:
<div className="bg-white rounded-xl border border-[#e8e6e1] overflow-hidden">
  <div className="px-5 py-4 border-b border-[#e8e6e1]">
    <h3 className="font-semibold text-[#1a1a1a]">Title</h3>
    <p className="text-xs text-[#8c8c8c]">Subtitle or description</p>
  </div>
  <div className="p-5">
    {/* Card content */}
  </div>
</div>
Key details:
  • Background: bg-white
  • Border: border border-[#e8e6e1]
  • Border radius: rounded-xl
  • Header padding: px-5 py-4
  • Body padding: p-5
  • Header divider: border-b border-[#e8e6e1]

Tables

Tables follow a consistent structure with muted headers and hover states:
<table className="w-full">
  <thead className="bg-[#fafaf8] border-b border-[#e8e6e1]">
    <tr className="text-left text-xs font-medium text-[#8c8c8c] uppercase tracking-wide">
      <th className="px-4 py-3">Column A</th>
      <th className="px-4 py-3">Column B</th>
      <th className="px-4 py-3">Column C</th>
    </tr>
  </thead>
  <tbody className="divide-y divide-[#e8e6e1]">
    <tr className="hover:bg-[#f0efe9]">
      <td className="px-4 py-3 text-sm text-[#4a4a4a]">Value</td>
      <td className="px-4 py-3 text-sm text-[#4a4a4a]">Value</td>
      <td className="px-4 py-3 text-sm text-[#4a4a4a]">Value</td>
    </tr>
  </tbody>
</table>
Key details:
  • Header background: bg-[#fafaf8]
  • Header text: text-xs font-medium text-[#8c8c8c] uppercase tracking-wide
  • Cell padding: px-4 py-3
  • Cell text: text-sm text-[#4a4a4a]
  • Row dividers: divide-y divide-[#e8e6e1]
  • Hover state: hover:bg-[#f0efe9]

Status Badges

Four badge variants cover all status states in the app:

Success / Positive

Used for: matched, completed, overpaid, active.
<span className="px-2 py-0.5 rounded-full text-xs font-medium bg-[#f0f5ed] text-[#6b7c5c]">
  Completed
</span>

Error / Negative

Used for: failed, underpaid, critical, overdue.
<span className="px-2 py-0.5 rounded-full text-xs font-medium bg-[#fef2f2] text-[#b45454]">
  Overdue
</span>

Warning / Pending

Used for: pending, in progress, expiring soon.
<span className="px-2 py-0.5 rounded-full text-xs font-medium bg-[#f5f0e6] text-[#8b7355]">
  Pending
</span>

Neutral / Info

Used for: inactive, informational, default.
<span className="px-2 py-0.5 rounded-full text-xs font-medium bg-[#fafaf8] text-[#8c8c8c]">
  Inactive
</span>

Badge Summary Table

VariantBackgroundText ColorUse Cases
Success#f0f5ed#6b7c5cCompleted, matched, active, overpaid
Error#fef2f2#b45454Failed, overdue, underpaid, critical
Warning#f5f0e6#8b7355Pending, in progress, expiring
Neutral#fafaf8#8c8c8cInactive, info, default

Buttons

Primary Action

<Button className="bg-[#6b7c5c] hover:bg-[#5a6b4d] text-white">
  Save Changes
</Button>

Secondary / Outline

<Button variant="outline" className="border-[#e8e6e1] hover:bg-[#f0efe9]">
  Cancel
</Button>

Destructive

<Button className="bg-[#b45454] hover:bg-[#9a4444] text-white">
  Delete
</Button>

Currency Display

Currency values use consistent formatting with color-coded positive/negative amounts:
// Positive amount
<span className="text-[#6b7c5c] tabular-nums">+$1,250.00</span>

// Negative amount
<span className="text-[#b45454] tabular-nums">-$85.50</span>

// Neutral amount
<span className="text-[#4a4a4a] tabular-nums">$500.00</span>
Always apply tabular-nums for proper alignment in tables and lists. Use the shared currency formatter:
const formatCurrency = (num: number): string => {
  const formatted = Math.abs(num).toLocaleString("en-US", {
    minimumFractionDigits: 2,
    maximumFractionDigits: 2,
  });
  return num < 0 ? `-$${formatted}` : `$${formatted}`;
};

Empty States

When a table or list has no data, show a centered message:
<div className="text-center py-12">
  <FileText className="w-10 h-10 text-[#8c8c8c] mx-auto mb-3" />
  <p className="text-sm text-[#8c8c8c]">No trips found</p>
  <p className="text-xs text-[#8c8c8c] mt-1">Import a CSV to get started</p>
</div>

Loading States

Use skeleton placeholders that match the content layout:
<div className="animate-pulse space-y-3">
  <div className="h-4 bg-[#e8e6e1] rounded w-1/3" />
  <div className="h-4 bg-[#e8e6e1] rounded w-2/3" />
  <div className="h-4 bg-[#e8e6e1] rounded w-1/2" />
</div>

Form Inputs

Form fields use the shadcn/ui Input and Label components with consistent styling:
<div className="space-y-1.5">
  <Label className="text-xs text-[#8c8c8c] uppercase tracking-wide">
    Vehicle Name
  </Label>
  <Input
    className="border-[#e8e6e1] focus:border-[#6b7c5c] focus:ring-[#6b7c5c]"
    placeholder="e.g., 2023 Tesla Model 3"
  />
</div>