Files
reterminal-dm4/chromium-setup/emmc-provisioning/PORTAL_STYLING_GUIDE.md

15 KiB
Raw Blame History

Portal Styling Guide (Template)

Use this document when building new portals so they match the visual and UX style of the reference portal (e.g. FreePBX / TM VOIP Extensions Portal). Replace placeholders like [Portal Name] with your portals name where relevant.


1. Design philosophy

  • Dark theme: Dark backgrounds with light text; no light-mode variant in this guide.
  • Accent: Single accent (teal/cyan gradient) for primary actions, links, and highlights.
  • Clarity: Clear hierarchy (cards, sections, labels), consistent spacing, readable typography.
  • Consistency: Same tokens, components, and patterns across all pages (login, app, modals).

2. Design tokens (CSS variables)

Define these in :root (or in a shared CSS file) and use them everywhere instead of hard-coded colors.

Colors

Token Value Usage
--bg-primary #0a0e14 Page background
--bg-secondary #11151c Header, secondary surfaces
--bg-tertiary #1a1f2b Inputs, table header, hover states
--bg-card #151a24 Cards, modals
--accent-primary #00d4aa Primary accent (teal)
--accent-secondary #00b894 Accent variant, secondary accent
--accent-glow rgba(0, 212, 170, 0.15) Focus rings, subtle highlights
--text-primary #e6e8eb Main text
--text-secondary #8b949e Labels, secondary text
--text-muted #5c6370 Placeholders, disabled, hints
--border-color #2d333b Borders (cards, inputs, tables)
--danger #ff6b6b Errors, delete, destructive actions
--danger-glow rgba(255, 107, 107, 0.15) Danger focus/hover background
--warning #ffd93d Warnings
--success #00d4aa Success (can match accent)
--gradient-accent linear-gradient(135deg, #00d4aa 0%, #00b894 50%, #00cec9 100%) Primary buttons, logo text

Example :root block

:root {
    --bg-primary: #0a0e14;
    --bg-secondary: #11151c;
    --bg-tertiary: #1a1f2b;
    --bg-card: #151a24;
    --accent-primary: #00d4aa;
    --accent-secondary: #00b894;
    --accent-glow: rgba(0, 212, 170, 0.15);
    --text-primary: #e6e8eb;
    --text-secondary: #8b949e;
    --text-muted: #5c6370;
    --border-color: #2d333b;
    --danger: #ff6b6b;
    --danger-glow: rgba(255, 107, 107, 0.15);
    --warning: #ffd93d;
    --success: #00d4aa;
    --gradient-accent: linear-gradient(135deg, #00d4aa 0%, #00b894 50%, #00cec9 100%);
}

3. Typography

  • Body / UI font: 'Outfit', -apple-system, BlinkMacSystemFont, sans-serif
  • Monospace (data, code, IDs): 'JetBrains Mono', monospace

Load from Google Fonts:

<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&family=Outfit:wght@300;400;500;600;700&display=swap" rel="stylesheet">
  • Body: color: var(--text-primary); line-height: 1.6;
  • Labels: font-size: 0.85rem; font-weight: 500; color: var(--text-secondary); optional text-transform: uppercase; letter-spacing: 0.5px;
  • Card/section titles: font-size: 1.1rem; font-weight: 600;
  • Table header: font-size: 0.75rem0.8rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; color: var(--text-secondary);
  • Table body: font-size: 0.9rem; monospace for IDs/codes

4. Page layout

Global

  • Reset: * { margin: 0; padding: 0; box-sizing: border-box; }
  • Body: background: var(--bg-primary); color: var(--text-primary); min-height: 100vh; font-family: 'Outfit', ...

Background treatment (optional)

Subtle gradient overlay for depth:

body::before {
    content: '';
    position: fixed;
    inset: 0;
    background:
        radial-gradient(circle at 20% 20%, rgba(0, 212, 170, 0.03) 0%, transparent 50%),
        radial-gradient(circle at 80% 80%, rgba(0, 184, 148, 0.03) 0%, transparent 50%),
        linear-gradient(180deg, var(--bg-primary) 0%, var(--bg-secondary) 100%);
    pointer-events: none;
    z-index: -1;
}

Header (fixed)

  • Container: background: var(--bg-secondary); border-bottom: 1px solid var(--border-color); position: fixed; top: 0; left: 0; right: 0; z-index: 1000; optional backdrop-filter: blur(10px);
  • Top row: Logo left; status/user/actions right; padding: 1rem 2rem; display: flex; align-items: center; justify-content: space-between;
  • Tabs row: Under the top row; background: var(--bg-tertiary); padding: 0.5rem 2rem; border-top: 1px solid var(--border-color); horizontal flex, gap, overflow-x auto for small screens

Main content

  • Container: max-width: 1400px; margin: 0 auto; padding: 2rem; padding-top: calc(2rem + 140px); (offset for fixed header + tabs). On mobile reduce padding and increase top offset if header stacks.

  • Wrapper: flex, align-items: center; gap: 0.75rem;
  • Icon: Square (e.g. 40×40px), background: var(--gradient-accent); border-radius: 10px; optional box-shadow: 0 4px 20px var(--accent-glow); emoji or icon inside.
  • Title (h1): font-size: 1.5rem; font-weight: 600; background: var(--gradient-accent); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent;

6. Tabs (main navigation)

  • Tab button (default): padding: 0.75rem 1.5rem; background: transparent; border: none; color: var(--text-secondary); font-size: 0.95rem; font-weight: 500; border-radius: 8px; flex with icon + label, gap: 0.5rem;
  • Hover: color: var(--text-primary); background: var(--bg-tertiary);
  • Active: background: var(--gradient-accent); color: var(--bg-primary);
  • Tab content: display: none; by default; .tab-content.active { display: block; } optional fade-in animation.

7. Cards

  • Base: background: var(--bg-card); border: 1px solid var(--border-color); border-radius: 16px; padding: 1.5rem; margin-bottom: 1.5rem;
  • Card header: flex, justify-content: space-between; align-items: center; margin-bottom: 1.5rem; padding-bottom: 1rem; border-bottom: 1px solid var(--border-color);
  • Card title: font-size: 1.1rem; font-weight: 600; flex with icon + text, gap: 0.5rem;

8. Buttons

  • Base: padding: 0.75rem 1.5rem; border-radius: 8px; font-size: 0.9rem; font-weight: 500; inline-flex, align-items: center; gap: 0.5rem; transition: all 0.2s ease;
  • Primary: background: var(--gradient-accent); color: var(--bg-primary); border: none; hover: slight translateY(-2px); box-shadow: 0 4px 20px var(--accent-glow);
  • Secondary: background: var(--bg-tertiary); border: 1px solid var(--border-color); color: var(--text-primary); hover: border-color: var(--accent-primary);
  • Danger: background: transparent; border: 1px solid var(--danger); color: var(--danger); hover: background: var(--danger); color: white;
  • Disabled: opacity: 0.5; cursor: not-allowed;
  • Icon-only (small): e.g. 28×28px, border-radius: 6px; same semantic colors (e.g. .btn-remove with --danger).

9. Forms

  • Grid: display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem;
  • Form group: flex column, gap: 0.5rem;
  • Label: font-size: 0.85rem; font-weight: 500; color: var(--text-secondary); optional uppercase + letter-spacing
  • Input / select: padding: 0.75rem 1rem; background: var(--bg-tertiary); border: 1px solid var(--border-color); border-radius: 8px; color: var(--text-primary); font-size: 0.9rem; monospace for IDs/codes
  • Focus: outline: none; border-color: var(--accent-primary); box-shadow: 0 0 0 3px var(--accent-glow);
  • Placeholder: color: var(--text-muted);
  • Read-only: background: var(--bg-secondary); cursor: default;
  • Checkbox: accent-color: var(--accent-primary); (or custom size e.g. 18×18px)
  • Password field: wrapper with toggle button; input padding-right: 3rem; so toggle doesnt overlap text.

10. Tables

  • Container: overflow-x: auto; border-radius: 12px; border: 1px solid var(--border-color);
  • Table: width: 100%; border-collapse: collapse;
  • th / td: padding: 0.6rem 0.75rem; text-align: left; border-bottom: 1px solid var(--border-color); color: var(--text-primary);
  • th: background: var(--bg-tertiary); font-size: 0.75rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px;
  • tbody tr hover: background-color: var(--bg-tertiary);
  • Last row: tr:last-child td { border-bottom: none; }
  • Data cells: font-family: 'JetBrains Mono', monospace; font-size: 0.9rem;
  • Actions column: right-aligned; min-width for action buttons; .table-actions { display: flex; gap: 1rem; flex-wrap: wrap; }
  • Data table variant: .data-table with alternating row background (e.g. nth-child(even) subtle rgba(255,255,255,0.02)) and same hover.

11. Badges and status

  • Status badge (e.g. connection): flex, align-items: center; gap: 0.5rem; padding: 0.5rem 1rem; background: var(--bg-tertiary); border-radius: 20px; font-size: 0.85rem; border: 1px solid var(--border-color);
  • Status dot: 8×8px circle; .connected { background: var(--success); } .error { background: var(--danger); } optional pulse animation
  • Pill badge (e.g. extension ID): padding: 0.25rem 0.75rem; background: var(--accent-glow); color: var(--accent-primary); border-radius: 20px; font-weight: 500;
  • Tech badge (e.g. PJSIP/SIP): small, border-radius: 4px; font-size: 0.75rem; font-weight: 600; text-transform: uppercase; distinct colors per type (e.g. PJSIP blue, SIP purple)

12. Search and filters

  • Search box: wrapper relative; input padding: 0.6rem 1rem 0.6rem 2.5rem; width: 250px; same colors as form inputs; optional ::before search icon (e.g. 🔍) left: 0.75rem;
  • Filter checkbox: inline-flex, align-items: center; gap: 0.4rem; color: var(--text-secondary); accent-color: var(--accent-primary);

13. Empty and loading states

  • Empty state: text-align: center; padding: 3rem; color: var(--text-muted);
  • No results: same idea, padding: 2rem;
  • Loading: flex center, padding: 2rem; spinner (e.g. 32×32px border, border-top-color: var(--accent-primary); animation: spin 1s linear infinite;)

14. Modals

  • Overlay: position: fixed; inset: 0; background: rgba(0,0,0,0.7); z-index: 1000; flex center; display: none; .active { display: flex; }
  • Dialog: background: var(--bg-card); border: 1px solid var(--border-color); border-radius: 16px; padding: 2rem; max-width: 500px; width: 90%; optional scale-in animation
  • Modal header: flex, align-items: center; gap: 1rem; margin-bottom: 1.5rem;
  • Modal icon: e.g. 48×48px, background: var(--accent-glow); border-radius: 12px; centered content
  • Modal title: font-size: 1.25rem; font-weight: 600;
  • Detail sections: margin-bottom: 2rem; section title font-size: 1.1rem; color: var(--accent-primary); border-bottom: 1px solid var(--border-color); padding-bottom: 0.5rem;
  • Details grid: grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 1rem; each item: label (small, secondary) + value (primary)

15. Toasts (notifications)

  • Container: position: fixed; top: 1rem; right: 1rem; z-index: 1100; flex column, gap: 0.5rem;
  • Toast: padding: 1rem 1.5rem; background: var(--bg-card); border: 1px solid var(--border-color); border-radius: 8px; flex, align-items: center; gap: 0.75rem; max-width: 400px; slide-in animation
  • Success: border-left: 3px solid var(--success);
  • Error: border-left: 3px solid var(--danger);

16. Pagination

  • Wrapper: flex, justify-content: center; align-items: center; gap: 0.5rem; padding: 1rem; border-top: 1px solid var(--border-color);
  • Button: padding: 0.5rem 0.75rem; background: var(--bg-tertiary); border: 1px solid var(--border-color); border-radius: 6px; color: var(--text-primary); font-size: 0.85rem; min-width: 36px;
  • Hover: background: var(--accent-glow); border-color: var(--accent-primary); color: var(--accent-primary);
  • Active page: background: var(--accent-primary); color: var(--bg-primary);
  • Disabled: opacity: 0.4; cursor: not-allowed;
  • Info text: color: var(--text-secondary); font-size: 0.85rem; between prev/next

17. Login page

  • Layout: full viewport, flex center; padding: 2rem;
  • Card: same tokens as app cards; max-width: 400px; padding: 2.5rem; border-radius: 16px; box-shadow: 0 8px 32px rgba(0,0,0,0.3);
  • Logo: centered; icon (e.g. 64×64px) with gradient + glow; title with gradient text
  • Form: same form-group and input styles as app; full-width primary submit button
  • Error message: color: var(--danger); font-size: 0.9rem; above or below form
  • Use the same :root variables and body background so login and app feel like one product.

18. Responsive

  • Breakpoint: e.g. @media (max-width: 768px)
  • Header top: flex-direction: column; gap: 1rem; text-align: center; reduce padding
  • Tabs: reduce horizontal padding; allow horizontal scroll if needed
  • Container: reduce padding; increase padding-top if header height grows (e.g. calc(1rem + 180px))
  • Form grid: grid-template-columns: 1fr; for single column on small screens

19. Checklist for a new portal

  • Copy or recreate the :root design tokens.
  • Load Outfit and JetBrains Mono (or same weights).
  • Use the same header structure: logo + status/user + actions, then tabs.
  • Use .card, .card-header, .card-title for sections.
  • Use .btn, .btn-primary, .btn-secondary, .btn-danger for actions.
  • Use .form-grid, .form-group, and input/select styles for forms.
  • Use .table-container, table, .data-table and th/td styles for lists.
  • Use same modal overlay/dialog and toast styles.
  • Use same empty, loading, and error states.
  • Apply same login page layout and token usage.
  • Test at 768px width for basic responsive behavior.
  • Replace [Portal Name] and any product-specific labels in this guide for your portal.

20. Reference files (this repo)

File Purpose
static/css/main.css Full implementation of tokens, layout, components
app/templates/base.html App shell: fonts, header, tabs, container, toasts
app/templates/login.html Login layout and inline tokens (can be moved to main.css)
app/templates/tabs/_dashboard.html Example: cards, stats, table container
app/templates/tabs/_users.html Example: card, form-grid, form-group, buttons, table

For a new portal, you can copy main.css and adapt it (e.g. change :root if you need a different accent), then build your base template and pages to use the same class names and structure described above.