Files
BanGUI/Docs/Web-Design.md
Lukas 53d664de4f Add origin field and filter for ban sources (Tasks 1 & 2)
- Task 1: Mark imported blocklist IP addresses
  - Add BanOrigin type and _derive_origin() to ban.py model
  - Populate origin field in ban_service list_bans() and bans_by_country()
  - BanTable and MapPage companion table show origin badge column
  - Tests: origin derivation in test_ban_service.py and test_dashboard.py

- Task 2: Add origin filter to dashboard and world map
  - ban_service: _origin_sql_filter() helper; origin param on list_bans()
    and bans_by_country()
  - dashboard router: optional origin query param forwarded to service
  - Frontend: BanOriginFilter type + BAN_ORIGIN_FILTER_LABELS in ban.ts
  - fetchBans / fetchBansByCountry forward origin to API
  - useBans / useMapData accept and pass origin; page resets on change
  - BanTable accepts origin prop; DashboardPage adds segmented filter
  - MapPage adds origin Select next to time-range picker
  - Tests: origin filter assertions in test_ban_service and test_dashboard
2026-03-07 20:03:43 +01:00

23 KiB
Raw Blame History

Frontend Design — Rules & Guidelines

Rules and conventions every designer must follow when working on BanGUI. This document defines the visual language, component usage, and design principles for the entire application. Read this before creating your first mockup or writing your first style.

Design system: Microsoft Fluent UI


1. Design Philosophy

  • Clarity over decoration. Every visual element must serve a purpose. If it does not help the user read, navigate, or act — remove it.
  • Consistency over creativity. Reuse existing Fluent UI components and patterns before inventing new ones. A familiar interface lowers cognitive load.
  • Content first. BanGUI is a data-heavy monitoring tool. Design around the data — tables, charts, status indicators — not around ornamental chrome.
  • Quiet until necessary. The default state of the UI should feel calm and neutral. Reserve colour, motion, and elevation for moments that genuinely need attention (errors, warnings, active bans).
  • Accessible by default. Every design decision must pass WCAG 2.1 AA. Colour alone must never be the only way to convey information.

2. Theming & Colour

All colour decisions go through the Fluent UI theme system. Never hard-code hex values in components.

Theme Slots

Use semantic colour slots from ISemanticColors wherever possible. Semantic slots describe intent rather than appearance, which means the UI adapts automatically when the theme changes.

Intent Semantic slot Example usage
Primary action themePrimary Primary buttons, active nav items, links
Danger / error errorText, errorIcon Ban warnings, failed connections, validation errors
Success successIcon (extend with custom slot) Successful unban confirmation, server online status
Warning Shared colour yellow / yellowDark Blocklist import warnings, approaching limits
Neutral text neutralPrimary Body copy, table cell text
Secondary text neutralSecondary Metadata, timestamps, captions
Disabled disabledText, disabledBackground Inactive controls, unavailable jails
Surface bodyBackground, bodyStandoutBackground Page canvas, sidebar, card backgrounds

Custom Theme

BanGUI uses a single custom theme generated with the Fluent UI Theme Designer. The theme is defined once in code and consumed everywhere via ThemeProvider.

  • The primary colour must have a contrast ratio of at least 4.5 : 1 against white for text and 3 : 1 for large text and UI elements.
  • Provide a dark theme variant alongside the default light theme. Both must share the same semantic slot names — only the palette values differ.
  • Never reference Fluent UI palette slots (themeDarker, neutralLight, etc.) directly in components. Always go through semantic slots so theme switching works seamlessly.

Colour Rules

  • Never use inline hex or RGB values. Always reference theme tokens (theme.palette.* or theme.semanticColors.*).
  • Use the 9-step theme colour ramp (themeLighterAlt through themeDarker) for subtle tints and shades within the primary hue.
  • Use the 14-step neutral ramp (white through black) for backgrounds, borders, and text hierarchy.
  • Reserve the shared accent colours (red, yellow, green, blue) strictly for semantic meaning: errors, warnings, success, informational. Never use them decoratively.
  • When two adjacent regions need separation, prefer a 1 px neutralLight border or a subtle background shift (bodyStandoutBackground) over a heavy line.

3. Typography

Use the Fluent UI type ramp exclusively. Do not introduce custom font sizes.

Type Ramp

Token Size Weight Usage in BanGUI
FontSizes.size28 28 px Semibold (600) Page titles — "Ban Overview", "Jail Management"
FontSizes.size20 20 px Semibold (600) Section headers — "Ban List", "Server Settings"
FontSizes.size16 16 px Semibold (600) Card titles, panel headers, sub-section labels
FontSizes.size14 14 px Regular (400) Body text, table cell content, form labels, buttons
FontSizes.size12 12 px Regular (400) Metadata: timestamps, IP geolocation tags, ban counts, tooltips
FontSizes.size10 10 px Regular (400) Badges, disclaimer text, chart axis labels (use sparingly)

Typography Rules

  • Font family — Use the Fluent default (Segoe UI, with the standard fallback stack). Never override the font family unless a monospace context demands it (log output, regex patterns, IP addresses — use Consolas, "Courier New", monospace for those).
  • Weight — Only two weights in regular UI: Regular (400) for body text and Semibold (600) for headings and emphasis. Use Bold (700) only in exceptional data visualisation contexts.
  • Line height — Follow the recommended line heights from the Fluent type ramp (e.g., 14 px text → 20 px line-height, 20 px text → 28 px line-height).
  • Text colour hierarchy — Use neutralPrimary for primary text, neutralSecondary for secondary/supporting text, and neutralTertiary only for placeholder text.
  • Alignment — Left-align all text by default. Centre-align only short labels inside cards or stat badges. Never right-align body text.
  • Truncation — Long text (IP addresses, log paths, regex patterns) must truncate with an ellipsis and show the full content in a tooltip on hover.
  • Monospace contexts — IP addresses, regex patterns, log paths, code snippets, and jail filter expressions must always render in monospace for readability.

4. Layout & Spacing

Grid

  • Use a 12-column responsive grid. In Fluent UI React, prefer CSS Grid or Stack for layout rather than Fabric Core grid classes.
  • The main content area sits next to a fixed-width side navigation (240 px collapsed to 48 px on small screens).
  • Maximum content width: 1440 px, centred on ultra-wide monitors.

Breakpoints

Follow the Fluent UI breakpoint scale:

Name Range Layout behaviour
Small 320 479 px Single column, collapsed nav, stacked cards
Medium 480 639 px Single column, optional side nav overlay
Large 640 1023 px Two columns, side nav visible
Extra large 1024 1365 px Full layout, side nav + main + optional aside
XX-large 1366 1919 px Comfortable full layout
XXX-large 1920 px + Max-width content, extra breathing room

Spacing Scale

Use the Fluent 4 px base unit for all spacing. Common values:

Token Value Usage
s2 4 px Inline spacing between icon and label
s1 8 px Padding inside compact elements (badges, chips)
m 16 px Standard padding inside cards and panels
l1 20 px Gap between table rows / list items
l2 32 px Margin between sections on a page
xl 48 px Top margin for page content below the header

Spacing Rules

  • Never use arbitrary pixel values. Always snap to the 4 px grid (4, 8, 12, 16, 20, 24, 32, 40, 48…).
  • Padding inside a card or panel: 16 px on all sides.
  • Gap between cards in a grid: 16 px.
  • Gap between a page title and the first content block: 24 px.
  • Table cell padding: 12 px horizontal, 8 px vertical.

5. Elevation & Depth

Use Fluent UI depth levels to communicate layering. Do not create custom box-shadow values.

Level Token Usage in BanGUI
Depth 4 Depths.depth4 Cards, grid tiles, stat summaries on the dashboard
Depth 8 Depths.depth8 Command bars, dropdown menus, filter popups
Depth 16 Depths.depth16 Tooltips, hover cards (IP info popup), teaching callouts
Depth 64 Depths.depth64 Modal dialogs (ban/unban confirmation), side panels

Elevation Rules

  • A surface's depth must match its semantic importance. A card sits at depth 4; a dialog demanding user action sits at depth 64.
  • The page canvas has zero elevation — it is the baseline.
  • Only one modal level may be visible at a time. Never stack dialogs.
  • When a dropdown or popup opens, the rest of the interface should receive an overlay scrim (rgba(0, 0, 0, 0.4)) only for depth-64 surfaces (modals). Lighter popups (depth 816) dismiss on outside click without a scrim.

6. Motion & Animation

All animations follow Fluent motion principles. Motion should feel natural, purposeful, and quick.

Timing

Duration Token Usage
100 ms MotionDurations.duration1 Micro-interactions: button press, checkbox toggle
200 ms MotionDurations.duration2 Fade in/out of tooltips, small element transitions
300 ms MotionDurations.duration3 Panel slide-in, page transitions, list reordering
400 ms MotionDurations.duration4 Large surface entrance (full-screen modals, first-load hero)

Easing

Curve Token When to use
Decelerate MotionTimings.decelerate Elements entering the view (slide in, fade in)
Accelerate MotionTimings.accelerate Elements leaving the view (slide out, fade out)
Standard MotionTimings.standard Elements that reposition within the view
Linear MotionTimings.linear Opacity-only transitions (fade out before a drill-in)

Animation Patterns

Interaction Pattern Details
Opening a jail detail panel Slide right in Panel slides from right edge, content fades in after 100 ms delay
Banning / unbanning an IP Delete & Slide Row fades out (300 ms decelerate), remaining rows slide up (300 ms)
Switching dashboard tabs Tabs & Pivots Active indicator slides (300 ms), outgoing content slides out, incoming slides in with 100 ms delay
Navigating to a sub-page Drill In Old content fades out (100 ms linear), new content scales down in (300 ms decelerate)

Motion Rules

  • Never animate something the user did not trigger. Auto-refreshing data should appear without animation — just update in place.
  • Respect prefers-reduced-motion. When the OS or browser signals reduced motion, disable all non-essential animations and replace them with instant state changes.
  • No loading spinners longer than 1 second without explanation. If a network call exceeds 1 s, show a skeleton screen or a contextual loading message, not just a spinner.
  • Keep looping animations (pulsing status dots, progress bars) subtle and small-area. Never animate large surfaces continuously.

7. Iconography

  • Use the Fluent UI icon set (@fluentui/react-icons or the Fabric Core icon font). Do not introduce third-party icon libraries.
  • Icon size follows the adjacent text: 16 px icons next to 14 px body text, 20 px icons next to 1620 px headers.
  • Icons must always have a text label or an aria-label. Icon-only buttons are acceptable only in toolbars and compact table rows, and they must have a tooltip.
  • Use outline icon variants by default. Switch to filled only for active or selected states (e.g., filled star for a favourited jail).
  • Icon colour inherits from the text colour of its context. Override only for semantic purposes (red icon for errors, green for success).

8. Component Usage

Use Fluent UI React components as the building blocks. The following mapping shows which component to use for each BanGUI feature area.

Navigation

Element Fluent component Notes
Side navigation Nav Persistent on large screens, collapsible on small. Groups: Dashboard, Map, Jails, Config, History, Blocklists.
Breadcrumbs Breadcrumb Show on detail pages (Jail > sshd, History > IP detail).
Page tabs Pivot None currently (previous tabs removed).

Data Display

Element Fluent component Notes
Data tables DetailsList All ban tables, jail overviews, history tables. Enable column sorting, selection, and shimmer loading.
Stat cards DocumentCard or custom Stack card Dashboard status bar — server status, total bans, active jails. Use Depth 4.
Status indicators Badge / Icon + colour Server online/offline, jail running/stopped/idle.
Country labels Monospaced text + flag emoji or icon Geo data next to IP addresses.

Forms & Actions

Element Fluent component Notes
Primary actions PrimaryButton "Ban IP", "Save Configuration", "Run Import Now".
Secondary actions DefaultButton "Cancel", "Reset", "Clear Filters".
Danger actions PrimaryButton with danger styling "Unban All", "Flush Chain". Red theme override on themePrimary.
Text inputs TextField IP address entry, regex pattern input, search fields.
Dropdowns Dropdown Jail selector, time-range presets, log level picker.
Toggles Toggle Enable/disable jail, enable blocklist source, ignore-self toggle.
Confirmations Dialog Confirm ban, confirm unban-all, confirm delete actions. Always require explicit user action.

Feedback

Element Fluent component Notes
Success messages MessageBar (success) "IP 1.2.3.4 has been banned in jail sshd."
Error messages MessageBar (error) "Failed to connect to fail2ban server."
Warning messages MessageBar (warning) "Blocklist import encountered 12 invalid entries."
Loading states Shimmer Apply to DetailsList rows and stat cards while data loads.
Empty states Custom illustration + text "No bans recorded in the last 24 hours." Centre on the content area.
Tooltips Tooltip / TooltipHost Full IP info on hover, full regex on truncated text, icon-only button labels.

9. Tables & Data Grids

Tables are the primary UI element in BanGUI. They must be treated with extreme care.

  • Use Fluent UI's DetailsList for all tabular data.
  • Column widths: Give the widest anticipated content enough room. IP addresses need ~140 px, timestamps ~180 px, country codes ~80 px. Use minWidth and maxWidth on each column.
  • Row density: Use compact mode for tables with many rows (ban lists, history) and normal mode for tables with fewer rows (jail overview, blocklist sources).
  • Sorting: Every column with comparable data must be sortable. Default sort: newest first for time-based tables.
  • Selection: Table rows for actionable entities (banned IPs, jails) must support single selection that highlights the row and enables contextual actions in a command bar above.
  • Shimmer loading: When data is loading, show shimmer placeholder rows matching the expected column layout. Never show a blank table body.
  • Empty state: When the table has zero rows, display a centred message and icon inside the table frame (e.g., "No bans in this time range").
  • Sticky header: Table headers must stick to the top of the scrollable area so column labels are always visible.
  • Zebra striping: Do not use alternating row colours. Fluent UI relies on hover and selection highlights instead.
  • Pagination vs. virtual scrolling: For tables that may exceed 100 rows, use virtual scrolling (DetailsList supports this natively). Avoid traditional pagination.

10. Cards & Stat Blocks

The dashboard uses cards to display key figures (server status, total bans, active jails).

  • Each card is a contained surface at Depth 4 with 16 px padding on all sides.
  • Card layout: icon or status dot (left) + large numeric value (FontSizes.size28, semibold) + label (FontSizes.size12, neutralSecondary).
  • Cards arrange in a horizontal row using Stack with horizontal tokens and 16 px gap.
  • On small screens, cards stack vertically in a single column.
  • Do not put actions inside stat cards. They are read-only summaries. Actions belong in context menus or command bars.

11. World Map View

  • The map renders country outlines only — no fill colours, no satellite imagery, no terrain shading. Countries are transparent with neutral strokes.
  • The map is fully interactive: users can zoom in/out using mouse wheel or pinch gestures, and pan by dragging. Zoom range: 1× (full world) to 8× (regional detail).
  • Zoom controls: Three small buttons overlaid in the top-right corner provide zoom in (+), zoom out (), and reset view (⟲) functionality. Buttons use appearance="secondary" and size="small".
  • Countries with banned IPs display a count badge centred inside the country polygon. Use FontSizes.size14 semibold, themePrimary colour.
  • Countries with zero bans remain completely blank — no label, no tint.
  • On hover: country region gets a subtle neutralBackground3 fill (only if the country has data). On click: fill shifts to brandBackgroundHover and the companion table below filters to that country. Default state remains transparent.
  • The map must have a light neutral border (neutralStroke1) around its container, with borderRadius.medium.
  • Time-range selector above the map uses Select dropdown with quick presets (24 h, 7 d, 30 d, 365 d).

12. Forms & Inputs

  • Use TextField with a visible label above the input (not a floating label).
  • Every text input must show a description or placeholder that illustrates the expected format (e.g., 192.168.1.0/24).
  • Validation errors appear below the input using the built-in errorMessage prop — red text, small size (FontSizes.size12).
  • Group related fields inside a bordered Stack or a GroupedList with a section header.
  • Action buttons sit at the bottom-right of a form group, with primary on the right and secondary (Cancel) on the left.
  • For the Regex Tester: use a side-by-side layout — sample log line on the left, regex input on the right, match result highlighted below. Monospace font for both fields.

13. Feedback & Status

Server Status Bar

  • A thin horizontal bar at the very top of the content area (below the nav header).
  • Online: green dot + "fail2ban running — v0.11.2 — 14 jails — 231 total bans". Use FontSizes.size12, neutralSecondary.
  • Offline / unreachable: red dot + "fail2ban server unreachable" in errorText colour. Bar background shifts to a very light red tint.
  • This bar is non-dismissible and always visible.

Inline Feedback

  • After a successful action (ban, unban, save config): show a MessageBar of type success at the top of the current content area, auto-dismiss after 5 seconds.
  • After a failed action: show a MessageBar of type error, not auto-dismissed — the user must close it manually.
  • Warnings (import issues, validation hints) use MessageBar of type warning, not auto-dismissed.

14. Dark Theme

  • Every screen must look correct in both light and dark themes.
  • Design light theme first, then verify dark theme by swapping the palette in the Theme Designer.
  • Never assume white backgrounds. Always use bodyBackground or bodyStandoutBackground from the theme.
  • Never use black text directly. Always use neutralPrimary, which adapts to both themes.
  • Images, illustrations, and the world map must have transparent or theme-aware backgrounds that do not create harsh rectangles in dark mode.
  • Test contrast ratios in both themes — a colour that passes AA on white may fail on dark grey.

15. Accessibility

  • Colour contrast: All text must meet WCAG 2.1 AA minimums (4.5 : 1 for normal text, 3 : 1 for large text and UI components). Use the Fluent UI colour accessibility guide as reference.
  • Keyboard navigation: Every interactive element must be reachable via Tab and operable with Enter or Space. Sidebar navigation, table row selection, dropdown menus — all must work without a mouse.
  • Focus indicators: Use the default Fluent UI focus ring (2 px themePrimary outline). Never hide or override focus styles.
  • Screen readers: Every icon-only button has an aria-label. Tables use aria-sort on sortable columns. Status indicators have aria-live="polite" regions so screen readers announce changes.
  • Reduced motion: Wrap all custom animations in a prefers-reduced-motion media query and provide an instant fallback.
  • Touch targets: All interactive elements have a minimum tap target of 44 x 44 px on touch devices (achieved via padding if the visible element is smaller).

16. Design Tokens & File Organisation

  • All theme definitions live in a single file: theme/appTheme.ts.
  • All spacing, sizing, and z-index constants live in theme/tokens.ts.
  • Components consume tokens via the theme context — never import raw pixel values.
  • When a design requires a value not already in the token set, add it to the shared tokens file and document its purpose. Never define a one-off constant inside a component.
// theme/appTheme.ts
import { createTheme } from "@fluentui/react";

export const lightTheme = createTheme({
  palette: {
    themePrimary: "#0078d4",
    themeDarkAlt: "#106ebe",
    // ... generated via Theme Designer
  },
});

export const darkTheme = createTheme({
  palette: {
    themePrimary: "#2b88d8",
    themeDarkAlt: "#3aa0f3",
    // ...
  },
});
// theme/tokens.ts
export const spacing = {
  xs: 4,
  s: 8,
  m: 16,
  l: 24,
  xl: 32,
  xxl: 48,
} as const;

export const contentMaxWidth = 1440;
export const sideNavWidth = 240;
export const sideNavCollapsedWidth = 48;

17. Do-Not-Do List

Do Do Not
Use Fluent UI components for every standard control Build custom buttons, dropdowns, or modals from scratch
Reference theme tokens for all colours Hard-code hex values like #ff0000 in components
Follow the 4 px spacing grid Use arbitrary pixel values (13 px, 7 px, 19 px)
Provide a tooltip for every icon-only button Leave icons unlabelled and inaccessible
Use Shimmer for loading states Show a blank screen or a standalone spinner with no context
Design for both light and dark themes Default to white backgrounds assuming light mode only
Use DetailsList for all tabular data Use raw HTML <table> elements or a third-party data grid
Use semantic colour slots (errorText, bodyBackground) Use descriptive palette slots (red, neutralLight) directly
Use prefers-reduced-motion for all custom animation Force animation on users with motion sensitivities
Test with keyboard and screen reader before signing off Assume mouse-only usage

18. References