This document describes design principles, component usage, layout structure, and styling conventions for the TimeTracker web UI. Use it when adding or changing templates and static assets.
- Clarity — One primary action per block; labels and hierarchy make the next step obvious.
- Consistency — Use the same components and patterns across pages (page header, cards, empty states, buttons).
- Minimal friction — Reduce steps for the core flow: start timer → monitor → stop → review. First-class navigation for Timer and Time entries.
- Professional appearance — Clean spacing, readable typography, semantic color use, and accessible contrast.
- Accessibility — Keyboard navigation, focus visibility, ARIA where needed, and semantic HTML. See Frontend Quality Gates for a11y checks.
Use the page_header macro from app/templates/components/ui.html on every main page:
- Parameters:
icon_class,title_text,subtitle_text(optional),actions_html(optional),breadcrumbs(optional). - Usage: One h1-level title per page; put primary actions in
actions_html; usebreadcrumbsfor deep pages (e.g. list → detail → edit).
- Stat cards: Use
stat_cardfromcomponents/ui.htmlorcomponents/cards.htmlfor numeric summaries (e.g. total hours, entry count). Prefer a single row of compact stat cards on dashboards. - Info cards: Use
info_cardfor short text summaries when needed.
- Full empty state: Use
empty_stateorempty_state_with_featuresfromcomponents/ui.htmlfor list or section-level “no data” (icon, title, message, primary action). - Compact empty state: Use
empty_state_compactfor table body or inline “no results” (smaller icon and text, same structure). - Always provide a clear primary action (e.g. “Start timer”, “Create project”, “View all”).
- Primary: One main action per block (
btn btn-primary) — e.g. “Start Timer”, “Save”, “Log Time”. - Secondary: Alternative or cancel (
btn btn-secondary). - Danger: Destructive actions (
btn btn-danger). - Ghost: Low emphasis (
btn btn-ghost). Use for tertiary actions. - Sizes:
btn-sm,btn-lgwhen needed. Use consistent padding and touch targets (e.g. ≥ 44px for mobile).
- Labels: Use
form-label; mark required fields with*and optional with “(optional)” in helper text. - Inputs: Use
form-inputfromapp/static/src/input.css. Addform-input-errorfor validation error state. - Validation: Show inline errors below fields; use the existing toast system for submit/API errors.
- Pattern: Overlay + content panel; close on overlay click and Escape. Primary button submits; secondary/cancel closes.
- Components: Use
modalandconfirm_dialogfromcomponents/ui.html. Ensure focus is trapped inside when open and restored on close. - Start Timer modal: Same pattern; single primary “Start” action; progressive disclosure (project/client → task → notes/tags) where possible.
- Where: Authenticated layout in
app/templates/base.html:#fabDockis a singleposition: fixedcolumn (flex-direction: column-reverse) at the bottom-right, with shared CSS variables (--fab-size,--fab-gap,--fab-edge,--fab-menu-gap) for spacing. RTL mirrors to the bottom-left. - Controls: (1) Actions —
#unifiedActionsRoot/#unifiedActionsFabopens#unifiedActionsMenuabove the button; URLs come fromdata-*attributes on#fabDock. (2) Team chat (whenteam_chatis enabled) —#persistentChatWidget/#chatWidgetToggle;#chatWidgetPanelis a fixed overlay (z-index: 85) aligned to the viewport edge so dock items cannot stack on top of it. (3) AI Helper —#aiHelperRoot/#aiHelperFab(circular FAB, same footprint as chat/actions) opens the existing drawer/backdrop (ai-helper.js). - Behavior:
app/static/floating-actions.jstoggles the actions menu, handles outside click and Escape, and runs Start Timer (same#openStartTimer/ dashboard#start-timerfallback as before), Log Time, New Task, New Project, New Client, and Reports. While the menu is open,#fabDockgetsfab-dock--menu-openso other dock children fade and ignore pointer events. - Admin:
#fabDockcan usefab-dock--adminto lift above the admin version banner;body.fab-dock-adminadjusts the chat panel bottom offset the same way. - Legacy scripts:
app/static/global-fab.jsandapp/static/quick-actions.jsare no longer included frombase.html; the web hub is implemented in markup plusfloating-actions.js.
- Where:
app/templates/timer/_time_entries_list.html(included from the time entries overview). Editable Notes and Duration for rows the user may change (permissions match server rules: own entry or admin; duration also requires schedule edit permission and a completed entry withend_time). - Script:
app/static/time-entries-inline-edit.js(loaded fromtime_entries_overview.html). Saves withPUTorPATCHto/api/entry/<id>(session JSON, same-originfetch). Success shows a short green check; errors use the toast manager and revert the cell.
- Use the existing toast system for success, error, warning, and info. Support optional
actionLinkandactionLabelfor follow-up (e.g. “View time entries” after stopping the timer). - Document types and behavior in this file; avoid ad-hoc
alert()for user feedback.
- Content width: Main content is wrapped in a max-width container (
max-w-7xl, 1280px) and centered (mx-auto) so lines don’t stretch on large screens. Applied inbase.htmlto the main content area. - Grid: Use Tailwind grid for dashboards and two-column layouts: e.g.
grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3; main content oftenlg:col-span-2, sidebarlg:col-span-1. - Spacing: Use the design tokens in
app/static/src/input.css(--spacing-xsthrough--spacing-3xl). Prefergap-4for card groups,gap-6for sections,p-6for card padding. - Mobile navigation: From the
mdbreakpoint up, the primary nav is the sidebar (hidden md:flexon the sidebar aside). Belowmd, the sidebar is off-canvas (hamburger opens it with a backdrop). The primary small-screen shortcuts are the bottom bar inpartials/_bottom_nav.html(md:hidden,z-50, top border,pb-safefor the iOS home indicator). Main shell#mainContentusespb-16 md:pb-0so scrollable content clears the bar. The More tab opens a bottom sheet (backdrop + panel,z-[55]/z-[60]); open/close lives inapp/static/mobile.js(BottomNavMoreDrawer). Active tab styling usestext-primaryandbg-primary/10(and dark variants). Prefer inline SVG (Heroicons-style stroke paths) in that partial for bar icons to avoid an extra icon font dependency on the bar.
- Tailwind: Prefer Tailwind utility classes. Design tokens and component classes live in
app/static/src/input.css(e.g.form-input,btn,text-h1…text-caption, status and action classes). - Colors: Use semantic tokens and classes:
primary,text-text-light/text-text-dark,text-text-muted-light/text-text-muted-dark,bg-card-light/bg-card-dark,border-border-light/border-border-dark. Use status/action classes (status-active,action-success, etc.) for badges and feedback. - Typography: Use
.text-h1….text-h6for headings,.text-body/.text-body-smfor body text,.text-label/.text-captionfor labels and captions. Page title = h1; section = h2; card title = h3. - Dark mode: Supported via
dark:variants anddarkMode: 'class'in Tailwind config. Test both themes when changing UI.
- Escape: Closes modals and dropdowns. Implement in
base-init.jsand feature scripts. - Enter: Submits the primary form in modals and dialogs.
- Tab order: Logical and visible; ensure focus ring is visible (e.g.
focus:ring-2 focus:ring-primary). - Skip link: “Skip to content” is present in
base.html; keep it and ensure the target is the main content anchor.
| Area | Files |
|---|---|
| Base layout | app/templates/base.html |
| Mobile bottom nav (partial) | app/templates/partials/_bottom_nav.html |
| Mobile shell behavior | app/static/mobile.js |
| Design tokens / Tailwind | app/static/src/input.css, tailwind.config.js |
| Components | app/templates/components/ui.html, app/templates/components/cards.html |
| Dashboard | app/templates/main/dashboard.html, app/static/dashboard-enhancements.js (value dashboard, week comparison chart, …) |
| Timer flow | app/templates/timer/timer_page.html, Start Timer modal (dashboard), app/static/floating-timer-bar.js |
| Floating hub (actions, chat, AI) | app/templates/base.html, app/templates/components/persistent_chat_widget.html, app/static/floating-actions.js, app/static/ai-helper.js |
| Time entries | app/templates/timer/time_entries_overview.html, app/templates/timer/_time_entries_list.html, app/static/time-entries-inline-edit.js |
For accessibility and quality checks, see FRONTEND_QUALITY_GATES.md.