From 6bc4e872ca52e707bbdafc5ca168ee2f2771b80f Mon Sep 17 00:00:00 2001 From: M57 <41645758+Hy4ri@users.noreply.github.com> Date: Fri, 28 Aug 2026 14:55:53 +0300 Subject: [PATCH] docs: add DESIGN.md design system specification (#1000) --- AGENTS.md | 9 +- CONTRIBUTING.md | 3 +- DESIGN.md | 305 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 315 insertions(+), 2 deletions(-) create mode 100644 DESIGN.md diff --git a/AGENTS.md b/AGENTS.md index 86820ea2..ec463978 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,7 +1,12 @@ # AGENTS.md Guidance for AI coding agents working on this repository. -This complements [README.md](README.md) (for humans) with agent-focused context. +This complements [README.md](README.md) (for humans) and [DESIGN.md](DESIGN.md) +(architecture & design rationale) with agent-focused context. Before making any +non-trivial architectural change — new screens, navigation changes, theme/state +patterns, module boundaries — read `DESIGN.md` first and keep changes consistent +with the decisions recorded there. If a change would contradict `DESIGN.md`, +flag it explicitly rather than silently diverging. ## Project Overview @@ -311,6 +316,8 @@ com.m57.hermescontrol/ ## Further Reading - [README.md](README.md) — human-facing overview, features, screenshots, tech stack +- [DESIGN.md](DESIGN.md) — architecture and design decisions, rationale for the + patterns in this doc (Navigation3, HermesScaffold, theme presets, etc.) - [CONTRIBUTING.md](CONTRIBUTING.md) — contributor workflow, PR checklist, code style - [.github/workflows/android.yml](.github/workflows/android.yml) — CI pipeline source of truth - [.github/workflows/merge-conflict-detector.yml](.github/workflows/merge-conflict-detector.yml) — auto-labels conflicting PRs diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 49c5c7ed..28a84dee 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -63,7 +63,7 @@ always creating a blank new one. If you use AI coding tools (including agents) to contribute: - **Never** add the AI tool as author, co-author, or `Co-Authored-By` in commit metadata. -- Direct AI agents to [`AGENTS.md`](AGENTS.md) in the repo root — it contains agent-specific guidance on project conventions, build quirks, and security considerations that complement this guide. +- Direct AI agents to [`AGENTS.md`](AGENTS.md) and [`DESIGN.md`](DESIGN.md) in the repo root — `AGENTS.md` contains operational conventions, build quirks, and testing rules, while `DESIGN.md` defines the normative design tokens, layout rules, and component styling contracts. --- @@ -121,5 +121,6 @@ Before submitting your PR, please verify: - [ ] No unused imports, unused parameters, or dead code. - [ ] Every `Image` and `Icon` element has a descriptive `contentDescription` for accessibility. - [ ] New screens use `HermesScaffold` and implement Loading/Error/Empty states. +- [ ] UI changes follow [`DESIGN.md`](DESIGN.md) (no emoji status glyphs, no FABs, verified >=3:1 contrast). - [ ] New components match the UI/UX style of similar existing screens (28+ screens for reference). - [ ] Commit messages follow [Conventional Commits](https://www.conventionalcommits.org/) and are atomic (subject + ≤2 lines body). diff --git a/DESIGN.md b/DESIGN.md new file mode 100644 index 00000000..d089628b --- /dev/null +++ b/DESIGN.md @@ -0,0 +1,305 @@ +--- +version: "alpha" +name: "Hermes Mobile Design System" +description: "High-density, minimal Material 3 interface for the Hermes Agent Android client" +omitted: [] +colors: + # Slate Dark Baseline (DefaultScheme.kt) + bg-root: "#0F1416" + bg-surface: "#0F1416" + bg-surface-container-lowest: "#0F1416" + bg-surface-container-low: "#171C1E" + bg-surface-container: "#1B2022" + bg-surface-container-high: "#252B2D" + bg-surface-container-highest: "#303638" + bg-surface-variant: "#171C1E" + + # Foreground / Text Tokens + text-primary: "#DEE3E5" + text-secondary: "#CEE7EC" + text-muted: "#BEC8CA" + text-on-primary: "#0F1416" + text-on-secondary: "#0F1416" + text-on-tertiary: "#0F1416" + text-outline: "#889294" + + # Accents & Containers (Slate Bright Dark Mode) + primary: "#80D5D2" + primary-container: "#303638" + on-primary-container: "#DEE3E5" + secondary: "#B2CBD0" + secondary-container: "#252B2D" + on-secondary-container: "#CEE7EC" + tertiary: "#A3C9EC" + tertiary-container: "#303638" + on-tertiary-container: "#DEE3E5" + + # Semantic Status Tokens (HermesStatusColors.kt & DefaultScheme.kt) + status-success: "#81D692" + status-success-container: "#252B2D" + status-on-success: "#0F1416" + status-warning: "#ECC248" + status-warning-container: "#252B2D" + status-on-warning: "#0F1416" + status-error: "#FFB4AB" + status-error-container: "#252B2D" + status-on-error: "#0F1416" + status-on-error-container: "#FFB4AB" + status-info: "#8ECEFF" + status-info-container: "#252B2D" + status-on-info: "#0F1416" + + # Code Block & Terminal Tokens (Color.kt) + terminal-bg: "#1E1E1E" + terminal-border: "#333333" + terminal-text: "#D4D4D4" + terminal-muted: "#808080" + code-keyword: "#569CD6" + code-string: "#CE9178" + code-comment: "#6A9955" + code-number: "#B5CEA8" + + # Diff Viewer Tokens + diff-add-bg: "rgba(61, 220, 132, 0.15)" + diff-add-text: "#3DDC84" + diff-delete-bg: "rgba(255, 92, 92, 0.15)" + diff-delete-text: "#FF5C5C" + +typography: + font-sans: "Roboto, 'Segoe UI', system-ui, sans-serif" + font-mono: "'JetBrains Mono', 'Roboto Mono', monospace" + + display-large: + fontFamily: "{typography.font-sans}" + fontSize: "44px" + fontWeight: 700 + lineHeight: 52px + letterSpacing: "-0.5px" + display-medium: + fontFamily: "{typography.font-sans}" + fontSize: "36px" + fontWeight: 700 + lineHeight: 44px + letterSpacing: "-0.25px" + display-small: + fontFamily: "{typography.font-sans}" + fontSize: "30px" + fontWeight: 600 + lineHeight: 38px + letterSpacing: "0px" + headline-large: + fontFamily: "{typography.font-sans}" + fontSize: "28px" + fontWeight: 600 + lineHeight: 34px + letterSpacing: "0px" + headline-medium: + fontFamily: "{typography.font-sans}" + fontSize: "24px" + fontWeight: 600 + lineHeight: 30px + letterSpacing: "0px" + headline-small: + fontFamily: "{typography.font-sans}" + fontSize: "20px" + fontWeight: 600 + lineHeight: 26px + letterSpacing: "0.1px" + title-large: + fontFamily: "{typography.font-sans}" + fontSize: "20px" + fontWeight: 600 + lineHeight: 26px + letterSpacing: "0.1px" + title-medium: + fontFamily: "{typography.font-sans}" + fontSize: "16px" + fontWeight: 500 + lineHeight: 22px + letterSpacing: "0.15px" + title-small: + fontFamily: "{typography.font-sans}" + fontSize: "14px" + fontWeight: 500 + lineHeight: 20px + letterSpacing: "0.1px" + body-large: + fontFamily: "{typography.font-sans}" + fontSize: "16px" + fontWeight: 400 + lineHeight: 24px + letterSpacing: "0.25px" + body-medium: + fontFamily: "{typography.font-sans}" + fontSize: "14px" + fontWeight: 400 + lineHeight: 20px + letterSpacing: "0.25px" + body-small: + fontFamily: "{typography.font-sans}" + fontSize: "12px" + fontWeight: 400 + lineHeight: 16px + letterSpacing: "0.4px" + label-large: + fontFamily: "{typography.font-sans}" + fontSize: "14px" + fontWeight: 500 + lineHeight: 20px + letterSpacing: "0.1px" + label-medium: + fontFamily: "{typography.font-sans}" + fontSize: "12px" + fontWeight: 500 + lineHeight: 16px + letterSpacing: "0.5px" + label-small: + fontFamily: "{typography.font-sans}" + fontSize: "11px" + fontWeight: 500 + lineHeight: 16px + letterSpacing: "0.5px" + +rounded: + none: "0dp" + extra-small: "4dp" + small: "8dp" + medium: "12dp" + large: "16dp" + extra-large: "28dp" + full: "9999dp" + +spacing: + xs: "2dp" + sm: "6dp" + md: "12dp" + lg: "18dp" + xl: "24dp" + xxl: "36dp" + +components: + top-app-bar: + backgroundColor: "{colors.bg-surface}" + textColor: "{colors.text-primary}" + height: "56dp" + chat-user-bubble: + backgroundColor: "{colors.primary}" + textColor: "{colors.text-on-primary}" + rounded: "{rounded.medium}" + padding: "10dp 14dp" + chat-assistant-bubble: + backgroundColor: "{colors.bg-surface-container}" + textColor: "{colors.text-primary}" + rounded: "{rounded.medium}" + padding: "10dp 14dp" + tool-chip: + backgroundColor: "{colors.bg-surface-container-high}" + textColor: "{colors.text-primary}" + rounded: "{rounded.full}" + padding: "4dp 10dp" + code-card: + backgroundColor: "{colors.terminal-bg}" + textColor: "{colors.terminal-text}" + rounded: "{rounded.small}" + padding: "8dp 12dp" +--- + +## Overview +Hermes Mobile (`com.m57.hermescontrol`) is a high-density, keyboard-first, distraction-free Android client for the Hermes Agent platform. The UI philosophy prioritizes information density, low battery drain (OLED-friendly), and instantaneous feedback over decorative visual noise. + +The visual language follows Material 3 guidelines strictly adapted for developer utilities: +- **Tone**: Technical, focused, utilitarian, crisp. +- **Form Factors**: Native Android phones, foldables, and tablets via adaptive Navigation 3 layouts. +- **Core Principle**: Zero fluff. Fast render cycles, explicit states, flat surfaces, and zero visual ambiguity. + +## Colors +The app supports 6 dynamic presets managed by `PaletteTemplate.kt` (`Default/Slate`, `Monochrome`, `Gruvbox`, `Catppuccin`, `AMOLED`, `Nord`), plus dynamic Android 12+ wallpaper theming: + +1. **Surfaces**: + - `bg-root`: Deep canvas background (`#0F1416` in Default Dark, `#000000` in AMOLED). + - `bg-surface`: Top app bars, bottom sheets, navigation drawer background. + - `bg-surface-container`: Assistant message bubbles, session list cards, settings group panels (`#1B2022`). + - `bg-surface-container-high`: Dialogs, interactive chips, elevated tool surfaces (`#252B2D`). + - `bg-surface-container-highest`: Selected containers and active pills (`#303638`). +2. **Accents & Tints**: + - Primary accent (`#80D5D2` Slate Bright Teal) is reserved for interactive controls, user bubbles, selected drawer icons, and active tabs. + - Selected navigation drawer items MUST tint icons directly to `primary` to prevent low-contrast grey-on-grey visual loss. +3. **Semantic Status Colors (`HermesStatusColors`)**: + - Status indicators (`success`, `warning`, `error`, `info`) must always satisfy a minimum **3:1 contrast ratio** against their respective container backgrounds (`onErrorContainer` on `errorContainer`). + - Every theme preset must explicitly define `onErrorContainer` (`#FFB4AB` in Default Dark) to prevent invisible text bugs. + +## Typography +- **UI Fonts**: System `Roboto` / sans-serif with strict semantic scaling to prevent layout shifts. +- **Code & Logs**: Tabular `JetBrains Mono` for terminal traces, tool payloads, JSON viewers, and memory tokens. +- **Scale Discipline (`Type.kt`)**: + - Screen titles: `20sp` (`title-large`) with 0.1sp tracking. + - Card headings: `16sp` (`title-medium`) with 0.15sp tracking. + - Chat content: `16sp` (`body-large`) with 24sp line-height for readability. + - Metadata & Subtitles: `14sp` (`body-medium`) or `12sp` (`body-small` / `label-medium`). + - Hero stats & Display numbers: `44sp` (`display-large`) down to `30sp` (`display-small`). + +## Layout +- **Scaffolding (`HermesScaffold`)**: + - Global `TopAppBar` containing screen title, connection indicator, profile switch dropdown, and quick action icon buttons. + - **No Floating Action Buttons (FABs)**: Primary triggers live in the top-bar actions or fixed bottom composer docks. + - **Scaffold Padding Foot-Gun Rule**: Content lambdas must **NEVER** apply `Modifier.padding(paddingValues)` on root children — the scaffold's internal `Box` already handles top-bar offsets. +- **Spacing Scale (`Spacing.kt`)**: + - `xs: 2dp`: Hairline gaps, micro dividers. + - `sm: 6dp`: Default inner padding, icon-to-text gap. + - `md: 12dp`: Card content, screen edge, section gaps. + - `lg: 18dp`: Hero spacing, between major sections. + - `xl: 24dp`: Full-feature spacing, large section break. + - `xxl: 36dp`: Maximum outer margin. +- **Controls & Navigation**: + - Horizontal chip bars must be single-row with `horizontalScroll()`. Never stack chips vertically into multi-row wraps. + - Dropdown pickers take precedence over cycle buttons to ensure all options are discoverable in one tap. + +## Elevation & Depth +- **Elevation Language**: Surface luminance layering (`surfaceContainer` tiers) and 1px borders (`outline` / `outlineVariant`) replace heavy drop shadows. +- **Flat Surface Rule**: + - Chat background is flat `colorScheme.background`. + - User message bubbles use solid `colorScheme.primary` (no multi-color gradients). + - Shimmer effects (`StateViews.kt`) are used strictly as animated loading states, never as static background decorations. + +## Shapes +- **Corner Radii Hierarchy (`Shapes.kt`)**: + - `0dp` (`rounded.none`): Terminal panels, full-bleed code blocks. + - `4dp` (`rounded.extra-small`): Chips, badges, inline toggles. + - `8dp` (`rounded.small`): Text fields, small cards, code cards. + - `12dp` (`rounded.medium`): Standard cards, list rows, message bubbles, dialogs. + - `16dp` (`rounded.large`): Large cards, bottom sheets. + - `28dp` (`rounded.extra-large`): Feature cards, full-bleed surfaces. + - `9999dp` (`rounded.full`): Interactive filter chips, timeline markers, circular icon buttons. + +## Components +- **Top App Bar**: Flat surface, 56dp height, contains title marquee, WS connection status pill, and action buttons. +- **Chat Timeline**: + - **User Bubbles**: Right-aligned, solid primary fill, high-contrast text (`onPrimary`). + - **Assistant Bubbles**: Left-aligned, `surfaceContainer` fill (`#1B2022`), full Markdown renderer support. + - **Tool Bubbles**: Expandable rows with tool vector icon, execution state indicator, parameter summary, and full payload bottom sheet. +- **Action & Filter Chips**: + - Must represent real system state (e.g., active model, selected profile, filter criteria). + - Active chips: `primaryContainer` fill with `onPrimaryContainer` text/icon. + - Inactive chips: `surfaceContainerHigh` fill with `text-secondary` text. + - Disabled controls: Visually greyed out with `alpha = 0.38f`. +- **Dialogs & Bottom Sheets**: + - Standard CRUD flows must provide all 4 operations (Create, Read, Update, Delete) with confirmation gates for destructive actions. + - Every managed resource must have a discoverable navigation surface in the UI. + +## Do's and Don'ts + +### Do's +- **DO** use official `androidx.compose.material.icons` vectors for every UI element and status indicator. +- **DO** use `Icons.AutoMirrored.Filled.*` for directional icons (`ArrowBack`, `Send`, `VolumeUp`, `MenuBook`). +- **DO** maintain $\ge 4.5:1$ text contrast and $\ge 3:1$ container contrast across all 6 presets. +- **DO** use Android string `` for count labels (`1 agent` vs `2 agents`). +- **DO** grey out disabled controls (`enabled = false`) visibly. +- **DO** ensure every created resource has a discoverable UI list or navigation tab. + +### Don'ts +- **DON'T** render emoji glyphs (`✅`, `❌`, `⭕`, `🔄`, `⚡`, `🛠`, `📄`) in the app UI — replace them with real vector icons. +- **DON'T** use Floating Action Buttons (FABs); anchor actions in top bars or fixed docks. +- **DON'T** apply `Modifier.padding(paddingValues)` inside `HermesScaffold` content lambdas. +- **DON'T** use gradient backgrounds on chat surfaces or message bubbles. +- **DON'T** create multi-layer nested cards or unnecessary decorative borders. +- **DON'T** use cycle buttons where a dropdown menu provides immediate option visibility.