Examples

These examples are intentionally small. They show the standard structure and class rhythm without binding teams to a single framework package.

Vue Admin Shell

Use this shape for authenticated Payd Labs dashboards and control planes.

<script setup lang="ts">
import { ref } from 'vue'
import { Menu, X, Moon, Sun, LogOut, LayoutDashboard } from 'lucide-vue-next'

const LOGO_LIGHT = 'https://res.cloudinary.com/dadkir6u2/image/upload/v1767808544/payd_logo_for_light_mode_scwwdc.png'
const LOGO_DARK = 'https://res.cloudinary.com/dadkir6u2/image/upload/v1767808543/payd_logo_for_dark_mode_otv7uv.png'

const isDark = ref(document.documentElement.classList.contains('dark'))
const mobileOpen = ref(false)

function toggleTheme() {
  isDark.value = !isDark.value
  document.documentElement.classList.toggle('dark', isDark.value)
}

const nav = [
  { label: 'Dashboard', to: '/', icon: LayoutDashboard },
]
</script>

<template>
  <div class="min-h-screen bg-surface text-text">
    <header class="fixed top-0 left-0 right-0 z-40 h-14 bg-surface/80 backdrop-blur-xl border-b border-border flex items-center px-4 lg:px-6">
      <button class="lg:hidden p-2 -ml-2 rounded-lg text-text-secondary hover:text-text hover:bg-surface-tertiary" @click="mobileOpen = !mobileOpen">
        <Menu v-if="!mobileOpen" :size="20" />
        <X v-else :size="20" />
      </button>
      <img :src="isDark ? LOGO_DARK : LOGO_LIGHT" alt="Payd" class="h-6 w-auto ml-2 lg:ml-0" />
      <span class="bg-accent-100 text-accent-dark px-2 py-0.5 rounded-md text-[10px] font-semibold font-heading ml-2">Product</span>
      <div class="flex-1" />
      <button class="p-2 rounded-lg text-text-tertiary hover:text-text hover:bg-surface-tertiary" @click="toggleTheme">
        <Moon v-if="!isDark" :size="16" />
        <Sun v-else :size="16" />
      </button>
      <button class="p-2 rounded-lg text-text-tertiary hover:text-error hover:bg-surface-tertiary ml-1">
        <LogOut :size="16" />
      </button>
    </header>

    <aside class="fixed top-14 bottom-0 left-0 w-56 bg-surface border-r border-border z-30 transition-transform duration-200"
      :class="mobileOpen ? 'translate-x-0' : '-translate-x-full lg:translate-x-0'">
      <nav class="p-3 space-y-0.5 mt-2">
        <router-link v-for="item in nav" :key="item.to" :to="item.to"
          class="flex items-center gap-2.5 px-3 py-2 rounded-lg text-[13px] font-medium transition-all text-text-secondary hover:text-text hover:bg-surface-tertiary">
          <component :is="item.icon" :size="16" />
          {{ item.label }}
        </router-link>
      </nav>
    </aside>

    <main class="pt-14 lg:pl-56">
      <div class="p-4 lg:p-6">
        <router-view />
      </div>
    </main>
  </div>
</template>

Login Panel

<div class="min-h-screen flex items-center justify-center bg-surface-secondary p-4">
  <div class="w-full max-w-sm">
    <div class="text-center mb-8">
      <img src="PAYD_LOGO" alt="Payd" class="h-7 w-auto mx-auto mb-3" />
      <div class="flex items-center justify-center gap-2">
        <h1 class="text-lg font-heading font-semibold text-text">Product</h1>
        <span class="bg-accent-100 text-accent-dark px-2 py-0.5 rounded-md text-[10px] font-semibold font-heading">Admin</span>
      </div>
      <p class="text-sm text-text-secondary mt-1">Product purpose</p>
    </div>

    <div class="bg-surface rounded-xl border border-border p-6 shadow-soft">
      <form class="space-y-4">
        <label class="block text-xs font-semibold text-text-secondary mb-1.5">Username</label>
        <input class="input" autocomplete="username" />
        <label class="block text-xs font-semibold text-text-secondary mb-1.5">Password</label>
        <input class="input" type="password" autocomplete="current-password" />
        <button class="w-full py-2.5 btn-accent text-sm">Sign In</button>
      </form>
    </div>
  </div>
</div>

Table Panel

<div class="bg-surface rounded-xl border border-border overflow-hidden">
  <div class="overflow-x-auto">
    <table class="w-full text-sm">
      <thead>
        <tr class="bg-surface-secondary border-b border-border">
          <th class="text-left px-4 py-2.5 font-heading font-semibold text-text-secondary text-xs uppercase tracking-wider">Reference</th>
          <th class="text-left px-4 py-2.5 font-heading font-semibold text-text-secondary text-xs uppercase tracking-wider">Status</th>
          <th class="text-left px-4 py-2.5 font-heading font-semibold text-text-secondary text-xs uppercase tracking-wider">Amount</th>
        </tr>
      </thead>
      <tbody>
        <tr class="border-b border-border/50 last:border-0 hover:bg-surface-secondary/50">
          <td class="px-4 py-2.5 font-mono text-xs text-accent-dark">STB-1048</td>
          <td class="px-4 py-2.5">
            <span class="px-2 py-0.5 rounded-full text-[10px] font-semibold bg-accent-100 text-accent-dark">completed</span>
          </td>
          <td class="px-4 py-2.5 font-mono text-xs">250.00 USDT</td>
        </tr>
      </tbody>
    </table>
  </div>
</div>

Status Pill Function

export function statusClass(status: string) {
  const value = status.toLowerCase()
  if (['approved', 'active', 'settled', 'completed', 'delivered'].includes(value)) {
    return 'bg-accent-100 text-accent-dark'
  }
  if (['pending', 'confirming', 'processing', 'received', 'awaiting_deposit'].includes(value)) {
    return 'bg-blue-50 text-blue-600 dark:bg-blue-500/10 dark:text-blue-400'
  }
  if (['expired', 'underpaid'].includes(value)) {
    return 'bg-amber-50 text-amber-600 dark:bg-amber-500/10 dark:text-amber-400'
  }
  if (['failed', 'rejected', 'suspended', 'settlement_failed'].includes(value)) {
    return 'bg-red-50 text-red-600 dark:bg-red-500/10 dark:text-red-400'
  }
  return 'bg-surface-tertiary text-text-tertiary'
}