archy/.cursor/rules/APP-UI-STANDARDS.md
Dorian 47a56e2212 Enhance UI styling and components for Archipelago
- Updated the glass-card and glass-button styles for improved aesthetics and functionality, including gradient borders and hover effects.
- Introduced new info-card and info-card-button components with enhanced styling and interactive features.
- Refactored existing HTML structure to utilize new card components, improving consistency across the UI.
- Enhanced button interactions for better user experience during settings and logs access.
2026-02-03 21:49:30 +00:00

15 KiB
Raw Blame History

Archipelago App UI Standards - For Apps Without Native UIs

Version: 1.0
Last Updated: 2026-02-03

Overview

This document defines the standard UI pattern for containerized applications that don't have their own web interface (e.g., Bitcoin Core, LND, Core Lightning, mempool backend services, etc.).

These UIs provide a simple, elegant way to:

  • Monitor service status and metrics
  • View connection information (RPC, REST, gRPC endpoints)
  • Access logs and settings
  • Copy configuration details for external tools

Architecture

┌─────────────────────────────────────┐
│ Nginx Container (Alpine)            │
│ - Serves static HTML/CSS/JS         │
│ - Port 8XXX (unique per service)    │
│ - Optional: Proxies RPC/API calls   │
└─────────────────────────────────────┘

File Structure

docker/
├── {service-name}-ui/
│   ├── index.html          # Main UI file
│   ├── Dockerfile          # Container build
│   ├── nginx.conf          # Nginx config
│   ├── {service-icon}      # App icon (svg/webp/png)
│   └── bg-{theme}.jpg      # Background image

Standard UI Components

1. CSS Class System

.glass-card - Main Container Cards

Used for: Header, main sections, modals

.glass-card {
    position: relative;
    background: rgba(0, 0, 0, 0.60);
    backdrop-filter: blur(24px);
    -webkit-backdrop-filter: blur(24px);
    box-shadow:
        0 8px 24px rgba(0, 0, 0, 0.45),
        inset 0 1px 0 rgba(255, 255, 255, 0.22);
    border-radius: 1rem;
    overflow-x: hidden;
    overflow-y: visible;
    border: none;
}

.glass-card::before {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: inherit;
    padding: 2px;
    background: linear-gradient(135deg, rgba(0, 0, 0, 0.8), transparent);
    -webkit-mask: 
        linear-gradient(#fff 0 0) content-box, 
        linear-gradient(#fff 0 0);
    -webkit-mask-composite: xor;
    mask-composite: exclude;
    pointer-events: none;
    z-index: 1;
}

.glass-card > * {
    position: relative;
    z-index: 2;
}

.info-card - Stat Display Cards

Used for: Status badges, metric displays (non-interactive)

.info-card {
    position: relative;
    background: rgba(0, 0, 0, 0.60);
    backdrop-filter: blur(24px);
    -webkit-backdrop-filter: blur(24px);
    box-shadow:
        0 8px 24px rgba(0, 0, 0, 0.45),
        inset 0 1px 0 rgba(255, 255, 255, 0.22);
    border-radius: 16px;
    padding: 12px;
    border: none;
}

.info-card::before {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: inherit;
    padding: 2px;
    background: linear-gradient(135deg, rgba(0, 0, 0, 0.8), transparent);
    -webkit-mask: 
        linear-gradient(#fff 0 0) content-box, 
        linear-gradient(#fff 0 0);
    -webkit-mask-composite: xor;
    mask-composite: exclude;
    pointer-events: none;
}

No hover effects - These are display-only elements.

.info-card-button - Interactive Action Buttons

Used for: Primary action buttons (Copy Info, View Logs, etc.)

.info-card-button {
    /* Same base styles as .info-card */
    position: relative;
    background: rgba(0, 0, 0, 0.60);
    backdrop-filter: blur(24px);
    -webkit-backdrop-filter: blur(24px);
    box-shadow:
        0 8px 24px rgba(0, 0, 0, 0.45),
        inset 0 1px 0 rgba(255, 255, 255, 0.22);
    border-radius: 16px;
    padding: 12px;
    transition: all 0.3s ease;
    border: none;
    cursor: pointer;
    color: rgba(255, 255, 255, 0.9);
}

.info-card-button::before {
    /* Same gradient as .info-card */
    content: '';
    position: absolute;
    inset: 0;
    border-radius: inherit;
    padding: 2px;
    background: linear-gradient(135deg, rgba(0, 0, 0, 0.8), transparent);
    -webkit-mask: 
        linear-gradient(#fff 0 0) content-box, 
        linear-gradient(#fff 0 0);
    -webkit-mask-composite: xor;
    mask-composite: exclude;
    pointer-events: none;
    transition: all 0.3s ease;
}

/* Hover state - lifts and brightens */
.info-card-button:hover {
    transform: translateY(-2px);
    background: rgba(0, 0, 0, 0.35);
    box-shadow:
        0 12px 32px rgba(0, 0, 0, 0.6),
        inset 0 1px 0 rgba(255, 255, 255, 0.25);
    color: rgba(255, 255, 255, 1);
}

.info-card-button:hover::before {
    background: linear-gradient(135deg, rgba(255, 255, 255, 0.3), transparent);
}

/* Active state - press down */
.info-card-button:active {
    transform: translateY(1px);
}

.glass-button - Secondary Buttons

Used for: Settings, Close (×), secondary actions

.glass-button {
    background-color: rgba(0, 0, 0, 0.6);
    backdrop-filter: blur(18px);
    -webkit-backdrop-filter: blur(18px);
    border: 1px solid rgba(255, 255, 255, 0.18);
    color: rgba(255, 255, 255, 0.9);
    transition: all 0.3s ease;
}

.glass-button:hover {
    color: white;
    background-color: rgba(0, 0, 0, 0.7);
}

Simple Info Rows - bg-white/5

Used for: Non-interactive info rows (RPC Host, Network, Status, etc.)

<div class="flex items-center justify-between p-3 bg-white/5 rounded-lg">
    <div class="flex items-center gap-3">
        <svg class="w-5 h-5 text-white/60"><!-- icon --></svg>
        <span class="text-white/80 text-sm">Label</span>
    </div>
    <span class="text-white/60 text-sm">Value</span>
</div>

No gradient borders - These are simple read-only display elements.


Standard Layout Pattern

1. Header Section (.glass-card)

<div class="glass-card p-6 mb-6">
    <div class="flex flex-col md:flex-row items-center md:items-center gap-4 md:gap-6">
        <!-- Logo (left) -->
        <div class="flex-shrink-0">
            <div class="logo-gradient-border">
                <img src="/assets/img/app-icons/{service-icon}" alt="{Service Name}" />
            </div>
        </div>
        
        <!-- Title and Description (center) -->
        <div class="flex-1 min-w-0">
            <h1 class="text-3xl font-bold text-white mb-2">{Service Name}</h1>
            <p class="text-white/70">{Service Description}</p>
        </div>

        <!-- Status Info (right) - OPTIONAL for headers with status -->
        <div class="w-full md:w-auto flex flex-col md:flex-row gap-3 md:gap-4">
            <div class="info-card flex items-center gap-3">
                <!-- Status info -->
            </div>
        </div>
    </div>
</div>

2. Quick Status Bar (.glass-card with .info-card grid)

<div class="glass-card p-6 mb-6">
    <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
        <div class="info-card flex items-center justify-between">
            <!-- Status indicator -->
        </div>
        <!-- ... more status cards -->
    </div>
</div>

3. Main Content Sections (.glass-card grid)

<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-8">
    <!-- Service 1: Node Status -->
    <div class="glass-card p-6">
        <div class="flex items-start gap-4 mb-4">
            <div class="flex-shrink-0 w-12 h-12 rounded-lg bg-white/10 flex items-center justify-center">
                <svg><!-- icon --></svg>
            </div>
            <div class="flex-1">
                <h2 class="text-xl font-semibold text-white mb-2">Section Title</h2>
                <p class="text-white/70 text-sm mb-4">Section description</p>
            </div>
        </div>

        <div class="space-y-3">
            <!-- Info rows (bg-white/5) -->
        </div>

        <button class="mt-4 w-full info-card-button text-sm font-medium" onclick="action()">
            Action Button
        </button>
    </div>
    
    <!-- ... more sections -->
</div>

4. Modals (.glass-card with backdrop)

<div class="modal hidden fixed inset-0 bg-black/80 backdrop-blur-sm z-50 items-center justify-center p-4" id="modalId">
    <div class="glass-card p-6 max-w-2xl w-full max-h-[80vh] overflow-y-auto">
        <div class="flex justify-between items-center mb-4">
            <h2 class="text-2xl font-bold text-white">Modal Title</h2>
            <button onclick="closeModal()" class="glass-button px-3 py-2 rounded-lg text-xl font-medium">×</button>
        </div>
        <!-- Modal content -->
    </div>
</div>

Visual Hierarchy

Container Importance (Most → Least)

  1. .glass-card - Main containers, sections, modals

    • Gradient border, strong blur (24px), inset highlights
  2. .info-card - Stat displays, status badges

    • Gradient border, backdrop blur, NO hover effects
  3. .info-card-button - Primary action buttons

    • Same as .info-card in default state
    • WITH hover effects (lift, brighten, enhanced gradient)
  4. bg-white/5 - Simple info rows

    • Dark background, NO borders, NO hover
  5. .glass-button - Secondary buttons

    • Simple glass effect, minimal hover

Port Assignments

Reserve unique ports for each service UI:

8334 - Bitcoin Knots UI
8081 - LND UI
8082 - Core Lightning UI (future)
8083 - Mempool UI (future)
8084 - BTCPay Server UI (future)
...

Update backend's docker_packages.rs to map these ports:

} else if app_id == "lnd" {
    Some("http://localhost:8081".to_string())
} else if app_id == "bitcoin-knots" {
    Some("http://localhost:8334".to_string())
}

Dockerfile Template

FROM docker.io/library/nginx:alpine

# Copy the HTML file
COPY index.html /usr/share/nginx/html/

# Create directories for assets
RUN mkdir -p /usr/share/nginx/html/assets/img/app-icons && \
    mkdir -p /usr/share/nginx/html/assets/img

# Copy assets
COPY {service-icon} /usr/share/nginx/html/assets/img/app-icons/
COPY bg-{theme}.jpg /usr/share/nginx/html/assets/img/

# Copy nginx config
COPY nginx.conf /etc/nginx/conf.d/default.conf

EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]

Nginx Config Template

Simple Static Serving (LND, most services)

server {
    listen 8XXX;  # Unique port for this service
    server_name _;

    root /usr/share/nginx/html;
    index index.html;

    location / {
        try_files $uri $uri/ /index.html;
    }
}

With RPC Proxy (Bitcoin Knots)

server {
    listen 8334;
    server_name _;

    root /usr/share/nginx/html;
    index index.html;

    # RPC proxy to avoid CORS issues
    location /bitcoin-rpc/ {
        proxy_pass http://127.0.0.1:8332/;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Authorization "Basic {BASE64_ENCODED_CREDS}";
        add_header Access-Control-Allow-Origin *;
        add_header Access-Control-Allow-Methods "POST, GET, OPTIONS";
        add_header Access-Control-Allow-Headers "Content-Type, Authorization";
        if ($request_method = OPTIONS) {
            return 204;
        }
    }

    location / {
        try_files $uri $uri/ /index.html;
    }
}

Deployment

Build and Run

# Build the image
cd docker/{service}-ui
sudo podman build -t {service}-ui:latest .

# Run the container
sudo podman run -d \
  --name {service}-ui \
  --restart unless-stopped \
  --network=host \
  --label 'com.archipelago.parent-app={service-id}' \
  {service}-ui:latest

Backend Integration

Update core/archipelago/src/container/docker_packages.rs:

} else if app_id == "{service-id}" {
    Some("http://localhost:8XXX".to_string())
}

Reference Implementations

Bitcoin Knots UI

  • Location: docker/bitcoin-ui/
  • Port: 8334
  • Features:
    • Live sync status with animations
    • RPC proxy for CORS handling
    • Real-time block updates
    • Connection info display

LND UI

  • Location: docker/lnd-ui/
  • Port: 8081
  • Features:
    • Node status monitoring
    • Channel count display
    • REST API + gRPC info
    • Settings and logs modals

Benefits of This Approach

  1. Consistency - All service UIs look and feel the same
  2. Lightweight - Nginx Alpine base (~10MB)
  3. Fast Development - Copy template, customize content
  4. Mobile Responsive - Works on all screen sizes
  5. Low Resource Usage - Static files, minimal CPU/RAM
  6. Easy Maintenance - Single pattern to update globally

Creating a New Service UI

Step-by-Step Process

  1. Create Directory Structure

    mkdir -p docker/{service}-ui
    cd docker/{service}-ui
    
  2. Copy Template Files

    cp ../bitcoin-ui/index.html ./
    cp ../bitcoin-ui/Dockerfile ./
    cp ../bitcoin-ui/nginx.conf ./
    
  3. Customize index.html

    • Update title, service name, description
    • Modify status cards for your service's metrics
    • Update connection info sections (RPC/REST/gRPC)
    • Adjust modal content
  4. Copy Assets

    cp ../../neode-ui/public/assets/img/app-icons/{service-icon} ./
    cp ../../neode-ui/public/assets/img/bg-{theme}.jpg ./
    
  5. Update Nginx Config

    • Set unique port number
    • Add RPC proxy if needed
  6. Update Dockerfile

    • Update asset COPY commands
    • Verify port EXPOSE
  7. Build and Deploy

    # Deploy to dev server
    sshpass -p "archipelago" rsync -avz --delete ./ archipelago@192.168.1.228:/tmp/{service}-ui-build/
    
    # Build on server
    sshpass -p "archipelago" ssh archipelago@192.168.1.228 \
      "cd /tmp/{service}-ui-build && \
       sudo podman build -t {service}-ui:latest . && \
       sudo podman stop {service}-ui 2>/dev/null || true && \
       sudo podman rm {service}-ui 2>/dev/null || true && \
       sudo podman run -d --name {service}-ui --restart unless-stopped \
       --network=host --label 'com.archipelago.parent-app={service-id}' \
       {service}-ui:latest"
    
  8. Update Backend

    • Edit core/archipelago/src/container/docker_packages.rs
    • Add port mapping for your service

Testing Checklist

  • UI loads correctly at http://{server}:8XXX/
  • Logo displays properly
  • Background image loads
  • All status cards show correct info
  • Buttons have proper hover effects
  • Modals open and close correctly
  • Mobile responsive (test on phone)
  • Glass effects render correctly
  • Gradient borders visible
  • Cache busting works (no stale content)

Future Enhancements

  • Live Data Updates - WebSocket connections for real-time status
  • Interactive Charts - Add Chart.js for visualizing metrics
  • Theme Variations - Allow users to select background themes
  • Dark/Light Mode - Toggle between color schemes
  • Internationalization - Support multiple languages
  • Accessibility - Improve screen reader support

File Locations

  • UI Standards Doc: /Users/dorian/Projects/archy/.cursor/rules/APP-UI-STANDARDS.md
  • Global UI Standards: /Users/dorian/Projects/archy/.cursor/rules/UI-STANDARDS.md
  • Reference Implementations:
    • Bitcoin UI: /Users/dorian/Projects/archy/docker/bitcoin-ui/
    • LND UI: /Users/dorian/Projects/archy/docker/lnd-ui/

Version: 1.0
Maintained by: Archipelago Development Team
Last Updated: 2026-02-03