mirror of
https://github.com/actualbudget/actual.git
synced 2026-10-11 22:49:59 +00:00
* [AI] Add standard size system to Button, Text and Input components
* [AI] Migrate modals, settings, rules, schedules and sidebar text to standard sizes
* [AI] Migrate mobile, budget, manager, banksync and transactions text to standard sizes
* [AI] Migrate reports, formula and news text to standard sizes
* Update VRT screenshots
Auto-generated by VRT workflow
PR: #9002
* Revert "Update VRT screenshots"
This reverts commit 35c947ef54.
* [AI] Fix line-height rendering as px strings in size tokens
* Update VRT screenshots
Auto-generated by VRT workflow
PR: #9002
* [AI] Tighten bank sync mobile row gap so larger labels fit on one line
* Update VRT screenshots
Auto-generated by VRT workflow
PR: #9002
* [AI] Restore amount display size to preserve underline spacing
* Update VRT screenshots
Auto-generated by VRT workflow
PR: #9002
* [AI] Pass caller-provided size through SingleAutocomplete render input
* [AI] Omit forced line-height on medium-sized controls to match the default look
* [AI] Document standard component sizes in DESIGN.md
* [AI] Adopt sidebar spacing tokens where values match exactly
* [AI] Scope the mobile touch-target rule to touch-primary control sizes
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
237 lines
12 KiB
Markdown
237 lines
12 KiB
Markdown
---
|
||
name: Actual Budget
|
||
description: Local-first personal finance, built around envelope budgeting
|
||
# All color values are the light-theme palette mapping; dark and midnight
|
||
# remap the same semantic roles via `theme.*` tokens (see The Semantic Token Rule).
|
||
colors:
|
||
actual-purple: '#8719e0'
|
||
actual-purple-hover: '#a368fc'
|
||
navy-ink: '#102a43'
|
||
navy-slate: '#627d98'
|
||
navy-mist: '#e8ecf0'
|
||
navy-frost: '#f7fafc'
|
||
page-text: '#272630'
|
||
surface-white: '#ffffff'
|
||
positive-green: '#147d64'
|
||
negative-red: '#e12d39'
|
||
link-blue: '#1980d4'
|
||
warning-gold: '#b88115'
|
||
typography:
|
||
display:
|
||
fontFamily: 'Inter Variable, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, sans-serif'
|
||
fontSize: '30px'
|
||
fontWeight: 600
|
||
headline:
|
||
fontFamily: 'Inter Variable, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, sans-serif'
|
||
fontSize: '20px'
|
||
fontWeight: 700
|
||
letterSpacing: '0.5px'
|
||
title:
|
||
fontFamily: 'Inter Variable, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, sans-serif'
|
||
fontSize: '15px'
|
||
fontWeight: 500
|
||
body:
|
||
fontFamily: 'Inter Variable, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, sans-serif'
|
||
fontSize: '16px'
|
||
fontWeight: 400
|
||
fontFeature: 'tnum, ss01, ss04'
|
||
label:
|
||
fontFamily: 'Inter Variable, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, sans-serif'
|
||
fontSize: '13px'
|
||
fontWeight: 400
|
||
rounded:
|
||
sm: '4px'
|
||
md: '6px'
|
||
spacing:
|
||
xs: '5px'
|
||
sm: '10px'
|
||
md: '16px'
|
||
lg: '20px'
|
||
components:
|
||
button-primary:
|
||
backgroundColor: '{colors.actual-purple}'
|
||
textColor: '{colors.surface-white}'
|
||
rounded: '{rounded.sm}'
|
||
padding: '5px 10px'
|
||
button-primary-hover:
|
||
backgroundColor: '{colors.actual-purple-hover}'
|
||
textColor: '{colors.surface-white}'
|
||
button-normal:
|
||
backgroundColor: '{colors.surface-white}'
|
||
textColor: '{colors.navy-ink}'
|
||
rounded: '{rounded.sm}'
|
||
padding: '5px 10px'
|
||
input:
|
||
backgroundColor: '{colors.surface-white}'
|
||
textColor: '{colors.page-text}'
|
||
rounded: '{rounded.sm}'
|
||
padding: '5px'
|
||
---
|
||
|
||
# Design System: Actual Budget
|
||
|
||
## 1. Overview
|
||
|
||
**Creative North Star: "The Home Office"**
|
||
|
||
Actual feels like a tidy personal desk: familiar, unpretentious, everything within reach. It is a tool people open weekly for years to reconcile accounts and move envelope money around, so the interface optimizes for routine, not first impressions. Density is moderate-to-high (this is a table-driven app), feedback is instant, and decoration is close to zero. The financial numbers are the interface; everything else exists to keep them legible and trustworthy.
|
||
|
||
The system explicitly rejects fintech-startup gloss (gradient heroes, glassmorphism, crypto-dashboard neon) and corporate banking UI (navy-and-gold enterprise-portal density). It is calm, trustworthy, and practical, and it must render identically well in three themes: light, dark, and midnight.
|
||
|
||
**Key Characteristics:**
|
||
|
||
- Table-first layouts with tabular numerals everywhere money appears
|
||
- One accent (Actual Purple) used sparingly for primary actions and selection
|
||
- Semantic color tokens only; components never hardcode hex values
|
||
- Utilitarian, compact components built for daily repetition
|
||
- Flat-first surfaces; shadows reserved for transient overlays
|
||
|
||
## 2. Colors
|
||
|
||
A calm navy-neutral base with one confident purple accent and strict semantic roles for money.
|
||
|
||
All hex values in this file (frontmatter included) are the light-theme palette mapping. The dark and midnight themes remap the same semantic roles to different palette values, so never use these hexes directly in components — route every color through the `theme.*` semantic tokens (see The Semantic Token Rule below).
|
||
|
||
### Primary
|
||
|
||
- **Actual Purple** (#8719e0): The single brand accent. Primary buttons, selected rows and borders, active navigation, mobile header. Hover lightens to **Lifted Purple** (#a368fc).
|
||
|
||
### Neutral
|
||
|
||
- **Navy Ink** (#102a43): Sidebar background, darkest text. The app's anchor dark.
|
||
- **Page Ink** (#272630): Default body text on light surfaces.
|
||
- **Navy Slate** (#627d98): Secondary and subdued text, table headers.
|
||
- **Navy Mist** (#e8ecf0): Page background, table borders, dividers.
|
||
- **Navy Frost** (#f7fafc): Hover rows, raised header rows.
|
||
- **Surface White** (#ffffff): Tables, cards, menus, modals.
|
||
|
||
### Tertiary (semantic money and status colors)
|
||
|
||
- **Positive Green** (#147d64): Positive amounts and funded budgets.
|
||
- **Negative Red** (#e12d39): Negative amounts, overspending, failures.
|
||
- **Link Blue** (#1980d4): Links and informational highlights.
|
||
- **Warning Gold** (#b88115): Underfunded templates, pending states.
|
||
|
||
### Named Rules
|
||
|
||
**The Semantic Token Rule.** Components never use raw palette values or hex codes. Every color goes through the `theme.*` semantic layer (`--color-*` custom properties) so all three themes (light, dark, midnight) stay correct. A hardcoded hex in a component is a bug.
|
||
|
||
**The One Accent Rule.** Actual Purple appears on well under 10% of any screen: primary action, current selection, active nav. It never decorates.
|
||
|
||
**The Never-Color-Alone Rule.** Positive/negative money state is conveyed by sign and context as well as color; green/red are reinforcement, never the only signal.
|
||
|
||
## 3. Typography
|
||
|
||
**Body Font:** Inter Variable (with system-ui fallback stack)
|
||
**Label/Mono behavior:** Same family; financial figures switch on tabular OpenType features
|
||
|
||
**Character:** One workhorse sans at compact sizes. No display serif, no font pairing games: hierarchy comes from weight and size, and the typographic star is the number column.
|
||
|
||
### Hierarchy
|
||
|
||
- **Display** (600, 30px): Page-level headings and large balances. Rare.
|
||
- **Headline** (700, 20px, 0.5px letter-spacing): Section titles.
|
||
- **Title** (500, 15px): Emphasized in-table and card text.
|
||
- **Body** (400, 16px): Default text.
|
||
- **Label** (400, 13px): Table meta, menus, dense secondary text; 12px and 10px steps exist for the tightest spots.
|
||
|
||
### Standard Component Sizes
|
||
|
||
`Button`, `Text` and `Input` accept a `size` prop: `small` | `medium` | `large` | `extra-large` (the `ComponentSize` vocabulary in `packages/component-library/src/tokens.ts`). Omitting the prop renders exactly as before — fully backward compatible. Sizes are responsive: they step up one notch on mobile/narrow for readability and touch targets, and collapse to the compact desktop scale from 512px/730px up. Breakpoint groups mirror the `useResponsive` view modes: narrow <512, small 512–729, medium 730–1099, wide ≥1100.
|
||
|
||
Text values (fontSize / lineHeight, px):
|
||
|
||
| Size | narrow | small | medium | wide |
|
||
| ----------- | ------- | ------- | ------- | ------- |
|
||
| small | 12 / 16 | 12 / 16 | 12 / 16 | 12 / 16 |
|
||
| medium | 13 / 18 | 13 / 18 | 13 / 18 | 13 / 18 |
|
||
| large | 16 / 22 | 15 / 20 | 15 / 20 | 15 / 20 |
|
||
| extra-large | 17 / 24 | 17 / 24 | 16 / 22 | 16 / 22 |
|
||
|
||
Control values for Button/Input (paddingY / paddingX / min-height, px):
|
||
|
||
| Size | narrow | small | medium | wide |
|
||
| ----------- | ------------ | ----------- | ----------- | ----------- |
|
||
| small | 3 / 8 / 24 | 3 / 8 / 24 | 3 / 8 / 24 | 3 / 8 / 24 |
|
||
| medium | 5 / 10 / — | 5 / 10 / — | 5 / 10 / — | 5 / 10 / — |
|
||
| large | 8 / 12 / 36 | 6 / 12 / 32 | 6 / 12 / 32 | 6 / 12 / 32 |
|
||
| extra-large | 10 / 14 / 40 | 8 / 12 / 36 | 8 / 12 / 36 | 8 / 12 / 36 |
|
||
|
||
`size="medium"` reproduces today's default control look exactly (5px/10px padding, 13px text, no min-height) and, on controls, inherits line-height like the default; the other sizes apply their scale line-height. `Text` always applies its size's line-height, while the omitted prop forces no line-height at all. `extra-large` narrow hits the 40px mobile touch target. The touch-target guarantee applies to touch-primary controls: at narrow, use `large` or `extra-large` for controls the user taps; `small` stays the compact 24px desktop-density choice at every breakpoint and is not meant for touch-primary controls.
|
||
|
||
### Named Rules
|
||
|
||
**The Tabular Number Rule.** Every standalone financial figure renders with `font-feature-settings: "tnum", "ss01", "ss04"` (via `FinancialText` or `styles.tnum`) so digits align in columns and disambiguate. A proportional-figure money column is a bug.
|
||
|
||
## 4. Elevation
|
||
|
||
Flat-first with ambient shadows. Structure comes from 1px borders (Navy Mist) and background shifts (White on Mist, Frost on hover), not depth. Shadows exist only to lift **transient** surfaces above the page: menus, tooltips, popovers, modals. Persistent surfaces like tables and cards sit flat or carry the faint card shadow at most.
|
||
|
||
### Shadow Vocabulary
|
||
|
||
- **Card** (`box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24)`): Resting cards and small raised widgets.
|
||
- **Ambient** (`box-shadow: 0 2px 4px 0 rgba(0,0,0,0.1)`): Focused inputs, subtle lift.
|
||
- **Large** (`box-shadow: 0 15px 30px 0 rgba(0,0,0,0.11), 0 5px 15px 0 rgba(0,0,0,0.08)`): Tooltips, popovers, floating panels.
|
||
|
||
### Named Rules
|
||
|
||
**The Transient-Only Rule.** If a surface stays on screen, it gets a border. If it appears on interaction and disappears, it may cast a shadow.
|
||
|
||
## 5. Components
|
||
|
||
Utilitarian and quick: compact paddings, instant state feedback, built for daily repetition. All values below are the light theme; every color routes through the semantic token layer.
|
||
|
||
### Buttons
|
||
|
||
- **Shape:** Gently rounded (4px radius), compact padding (5px 10px)
|
||
- **Primary:** Actual Purple background, white text; hover lifts to Lifted Purple with a soft shadow transition (`box-shadow .25s`)
|
||
- **Normal:** White background, Navy Ink text, 1px navy border
|
||
- **Bare:** Transparent, 5px padding; background tint on hover/press. The workhorse for in-table actions
|
||
|
||
### Inputs / Fields
|
||
|
||
- **Style:** White background, 1px border, 4px radius, 5px padding
|
||
- **Focus:** Border switches to the selected accent; big inputs drop the border and take the ambient shadow instead
|
||
- **Big variant:** 10px padding for mobile and prominent forms (40px minimum touch height on mobile)
|
||
|
||
### Cards / Containers
|
||
|
||
- **Corner Style:** 6px radius on table containers (top corners), 4px elsewhere
|
||
- **Background:** Surface White on Navy Mist page background
|
||
- **Shadow Strategy:** Card shadow at most; structure via 1px Navy Mist border
|
||
- **Internal Padding:** 16-20px
|
||
|
||
### Navigation
|
||
|
||
- **Sidebar:** Navy Ink background, light navy text; hover darkens the row, selection marked with a purple accent and purple text. Status states (pending/positive/failed) tint the item background gold/green/red
|
||
- **Mobile:** Purple header, white nav bar, purple selected item
|
||
|
||
### Pills / Chips
|
||
|
||
- **Style:** Editor pills: tinted background, 4px radius, 3px 5px padding. Used for rule conditions and inline tokens
|
||
|
||
### Signature Component: The Money Table
|
||
|
||
The core surface of the app. White rows on 1px Navy Mist borders, Frost hover, purple-bordered selection, sticky header with Slate 13px text, amounts right-aligned in tabular figures colored by the semantic money tokens. Alternate-row striping is theme-controlled, never hardcoded.
|
||
|
||
## 6. Do's and Don'ts
|
||
|
||
### Do:
|
||
|
||
- **Do** route every color through `theme.*` semantic tokens so light, dark, and midnight themes all work. Test all three.
|
||
- **Do** wrap standalone financial numbers in `FinancialText` or apply `styles.tnum`.
|
||
- **Do** keep components compact and quick: 4px radii, 5-10px paddings, instant hover/press states.
|
||
- **Do** reuse the existing component library (`@actual-app/components`) before writing new UI.
|
||
- **Do** keep Actual Purple scarce: primary action and selection only.
|
||
- **Do** respect the breakpoints: 512px (small), 730px (medium), 1100px (wide); mobile touch targets are at least 40px tall.
|
||
|
||
### Don't:
|
||
|
||
- **Don't** use fintech-startup gloss: gradient heroes, glassmorphism, neon accents, crypto-dashboard styling (PRODUCT.md anti-reference).
|
||
- **Don't** use corporate banking UI patterns: navy-and-gold, enterprise-portal density, legalese energy (PRODUCT.md anti-reference).
|
||
- **Don't** hardcode hex values or raw `--palette-*` colors in components; the semantic layer is the only entry point.
|
||
- **Don't** rely on color alone for positive/negative amounts.
|
||
- **Don't** put shadows on persistent surfaces; shadows are for transient overlays only.
|
||
- **Don't** use `border-left`/`border-right` thicker than 1px as a colored accent stripe.
|
||
- **Don't** animate layout properties; transitions are for color, opacity, and shadow (like the button's `box-shadow .25s`).
|