UI Design System
This document establishes the official UI design patterns and component standards for the DeployStack frontend. All new components and pages must follow these guidelines to ensure consistency and maintainability.
Design Principles
- Consistency: Use established patterns and components
- Accessibility: Follow WCAG guidelines and semantic HTML
- Responsiveness: Design for all screen sizes
- Performance: Optimize for fast loading and smooth interactions
- Maintainability: Write clean, reusable component code
Color System
Primary Colors
The DeployStack color palette uses teal as the primary brand color, ensuring WCAG compliance for accessibility.
/* Primary Brand Colors */
--primary: #0f766e; /* teal-700 - WCAG AA compliant on white */
--primary-hover: #115e59; /* teal-800 - WCAG AAA compliant on white */
WCAG Compliance
- Primary (teal-700): Contrast ratio 5.47:1 on white - ✅ AA Pass, ❌ AAA Fail
- Primary-hover (teal-800): Contrast ratio 7.58:1 on white - ✅ AA Pass, ✅ AAA Pass
This ensures our primary color meets accessibility standards, with enhanced contrast on hover states.
Text Colors
/* Light Mode / Dark Mode */
--text-primary: theme('colors.zinc.800') / theme('colors.zinc.100');
--text-secondary: theme('colors.zinc.600') / theme('colors.zinc.400');
Background Colors
/* Light Mode / Dark Mode */
--bg-primary: white / theme('colors.zinc.900');
--bg-secondary: theme('colors.zinc.50') / theme('colors.zinc.800');
Link Color Scheme
DeployStack implements intelligent link styling with automatic teal coloring for text links while preserving button styling:
Text Links
- Light mode:
text-teal-700
→hover:text-teal-800
(WCAG AA → AAA on hover) - Dark mode:
text-teal-400
→hover:text-teal-300
(Already WCAG AAA compliant) - Underlines:
decoration-teal-700/30
→hover:decoration-teal-700/60
- Focus states:
outline-teal-700
(light) /outline-teal-400
(dark)
Usage Examples
<!-- ✅ Gets teal link styling automatically -->
<a href="/about">Learn more about us</a>
<a href="https://example.com" class="font-semibold">External link</a>
<!-- ❌ Excluded from link styling (keeps button appearance) -->
<a href="/signup" class="rounded-md bg-primary px-4 py-2">Sign Up</a>
<a href="/login" class="button-primary">Login</a>
Color Usage Guidelines
- Primary Actions: Use
--primary
(teal-700) for primary buttons and key interactive elements - Hover States: Transition to
--primary-hover
(teal-800) for enhanced contrast - Text Links: Automatically styled with teal colors - no additional classes needed
- Button Links: Include styling classes (
bg-*
,rounded-*
) to maintain button appearance - Focus States: Ensure all interactive elements have visible focus indicators using the teal color scheme
Layout Design Patterns
Content Wrapper Pattern
DeployStack follows a mandatory content wrapper pattern for all tabbed content and detail pages. This pattern ensures visual consistency and proper content hierarchy throughout the application.
Design Requirements
The content wrapper pattern is required for:
- Team management pages
- MCP server installation pages
- Settings and configuration pages
- Any page using tabbed content with
DsTabs
- Detail views that need elevated content presentation
Implementation
Use the ContentWrapper
component for all qualifying pages:
<ContentWrapper>
<YourTabContent />
</ContentWrapper>
The wrapper provides:
- Gray background container (
bg-muted/50
) - Responsive max-width constraints
- White card elevation with proper spacing
- Consistent vertical rhythm
For complete implementation details, see the component source code at services/frontend/src/components/ContentWrapper.vue
.
Visual Hierarchy
This pattern creates a three-tier visual hierarchy:
- Page background - Default dashboard background
- Content container - Gray muted background wrapper
- Content card - White elevated card with content
This hierarchy is a design system requirement and must be followed consistently across all applicable pages.
Data Tables
For data table implementation, see the dedicated Table Design System guide.
For pagination implementation, see the Pagination Implementation Guide.
Badge Design Patterns
Badges are used for status indicators, categories, and metadata.
Status Badges
<Badge variant="default">Active</Badge>
<Badge variant="secondary">Inactive</Badge>
<Badge variant="destructive">Error</Badge>
<Badge variant="outline">Pending</Badge>
Category/Tag Badges
<Badge variant="secondary" class="font-mono text-xs">
{{ category.icon }}
</Badge>
Numeric Badges
<Badge variant="outline">
{{ item.sort_order }}
</Badge>
Form Design Patterns
Modal Forms
Use AlertDialog
for forms in modals:
<AlertDialog :open="isOpen" @update:open="(value) => isOpen = value">
<AlertDialogContent class="sm:max-w-[425px]">
<AlertDialogHeader>
<AlertDialogTitle>{{ modalTitle }}</AlertDialogTitle>
<AlertDialogDescription>
{{ modalDescription }}
</AlertDialogDescription>
</AlertDialogHeader>
<form @submit.prevent="handleSubmit" class="space-y-4">
<!-- Form fields -->
<div class="space-y-2">
<Label for="field-name">{{ t('form.field.label') }}</Label>
<Input
id="field-name"
v-model="formData.name"
:placeholder="t('form.field.placeholder')"
:class="{ 'border-destructive': errors.name }"
required
/>
<div v-if="errors.name" class="text-sm text-destructive">
{{ errors.name }}
</div>
</div>
<AlertDialogFooter>
<Button type="button" variant="outline" @click="handleCancel">
{{ t('form.cancel') }}
</Button>
<Button type="submit" :disabled="!isFormValid || isSubmitting">
{{ isSubmitting ? t('form.saving') : t('form.save') }}
</Button>
</AlertDialogFooter>
</form>
</AlertDialogContent>
</AlertDialog>
Form Field Pattern
<div class="space-y-2">
<Label for="field-id">{{ t('form.field.label') }}</Label>
<Input
id="field-id"
v-model="formData.field"
:placeholder="t('form.field.placeholder')"
:class="{ 'border-destructive': errors.field }"
@input="handleFieldChange"
/>
<div v-if="errors.field" class="text-sm text-destructive">
{{ errors.field }}
</div>
</div>
Button Patterns
Loading States
Buttons now include built-in loading state functionality. For comprehensive loading button documentation, see the Button Loading States Guide.
<!-- Button with loading state -->
<Button
:loading="isSubmitting"
loading-text="Saving..."
@click="handleSave"
>
Save Changes
</Button>
Primary Actions
<Button @click="handlePrimaryAction">
<Plus class="h-4 w-4 mr-2" />
{{ t('actions.add') }}
</Button>
Secondary Actions
<Button variant="outline" @click="handleSecondaryAction">
{{ t('actions.cancel') }}
</Button>
Destructive Actions
<Button
variant="destructive"
@click="handleDelete"
:loading="isDeleting"
loading-text="Deleting..."
class="bg-destructive text-destructive-foreground hover:bg-destructive/90"
>
<Trash2 v-if="!isDeleting" class="h-4 w-4 mr-2" />
{{ t('actions.delete') }}
</Button>
Icon-Only Buttons
<Button
variant="ghost"
size="icon"
:loading="isRefreshing"
>
<span class="sr-only">{{ t('actions.menu') }}</span>
<MoreHorizontal v-if="!isRefreshing" class="h-4 w-4" />
</Button>
Layout Patterns
Page Header
<div class="flex items-center justify-between">
<div>
<h1 class="text-2xl font-bold">{{ pageTitle }}</h1>
<p class="text-muted-foreground">{{ pageDescription }}</p>
</div>
<Button @click="handlePrimaryAction" class="flex items-center gap-2">
<Plus class="h-4 w-4" />
{{ t('actions.add') }}
</Button>
</div>
Content Sections
<div class="space-y-6">
<!-- Header -->
<div class="flex items-center justify-between">
<!-- Header content -->
</div>
<!-- Success/Error Messages -->
<Alert v-if="successMessage" class="border-green-200 bg-green-50 text-green-800">
<CheckCircle class="h-4 w-4" />
<AlertDescription>{{ successMessage }}</AlertDescription>
</Alert>
<!-- Main Content -->
<div class="space-y-4">
<!-- Content -->
</div>
</div>
Icon Usage
Standard Icon Sizes
- Small icons:
h-4 w-4
(16px) - for buttons, table actions - Medium icons:
h-5 w-5
(20px) - for form fields, navigation - Large icons:
h-6 w-6
(24px) - for page headers, prominent actions
Icon with Text
<Button>
<Settings class="h-4 w-4 mr-2" />
{{ t('actions.settings') }}
</Button>
Status Icons
<CheckCircle class="h-4 w-4 text-green-600" />
<AlertCircle class="h-4 w-4 text-yellow-600" />
<XCircle class="h-4 w-4 text-red-600" />
Responsive Design
Mobile-First Approach
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<!-- Responsive grid -->
</div>
Hide/Show on Different Screens
<div class="hidden md:block">Desktop only</div>
<div class="block md:hidden">Mobile only</div>
Accessibility Guidelines
Screen Reader Support
<Button>
<span class="sr-only">{{ t('actions.openMenu') }}</span>
<MoreHorizontal class="h-4 w-4" />
</Button>
Proper Labels
<Label for="input-id">{{ t('form.label') }}</Label>
<Input id="input-id" v-model="value" />
Focus Management
<Button
@click="handleAction"
:disabled="isLoading"
class="focus:ring-2 focus:ring-ring focus:ring-offset-2"
>
{{ t('actions.submit') }}
</Button>
Migration Guide
Updating Existing Tables
If you have an existing table using raw HTML elements, follow these steps:
-
Replace HTML elements with shadcn-vue components:
<table>
→<Table>
<thead>
→<TableHeader>
<tbody>
→<TableBody>
<tr>
→<TableRow>
<th>
→<TableHead>
<td>
→<TableCell>
-
Update imports:
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table'
-
Add proper empty state handling
-
Update action menus to use AlertDialog for destructive actions
-
Ensure proper badge usage for status indicators
For detailed migration strategies and architectural considerations, see the Frontend Architecture - Migration Guidelines.
Table Design System
Developer guide for implementing data tables in DeployStack frontend using shadcn-vue Table components.
Backend Development
Complete guide to developing and contributing to the DeployStack backend - a high-performance Node.js application built with Fastify, TypeScript, and extensible plugin architecture.