986d831186
- Overview tab: 3 donut pie charts (expenses by category, sub payments, invoiced) each with independent month navigation arrows - Expenses tab: big red total in category sidebar, left-aligned category column, white text - Subcontractors tab: big gold total in sidebar, pending/paid breakdown with gold highlight - Invoices tab: big green total in companies sidebar, dashboard-inline-link for invoice# and company, StatusBadge with Invoiced/Paid labels, column widths tuned - StatusBadge: add optional label prop for custom display text - Layout/style polish across tabs Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
20 KiB
20 KiB
Fourge Portal Layout System
This is the single source of truth for dashboard/profile visual structure and UI geometry.
1) Global Frame
- Viewport app shell:
height: 100vh,overflow: hidden - Main content gutter:
24pxall sides - Sidebar:
width: 76px,top: 24px,left: 24px,height: calc(100vh - 48px),border-radius: 8px - Main wrapper offset from sidebar:
margin-left: 100px - Page rhythm unit:
24px(header spacing, card gaps, section gaps)
2) Theme + Background
- Background ownership is
bodyviabackground: var(--bg). - Dark base token
--bgis full gradient:- radial glow + vertical dark gradient.
- Light base token
--bgis full gradient:- gray radial glow + white vertical gradient.
- Do not use
htmltheme-gradient scripting for Safari chrome behavior.
3) Tokens
- Accent:
#F5A523 - Card bg dark:
rgba(255,255,255,0.02) - Card bg light:
rgba(0,0,0,0.02) - Sidebar/menu shell uses the same transparent card background token as cards
- Secondary card tone dark:
rgba(255,255,255,0.08) - Secondary card tone light:
rgba(0,0,0,0.08) - Border dark:
rgba(245,165,35,0.15) - Border light:
rgba(0,0,0,0.1) - Text primary dark/light:
#ffffff / #0d0d0d - Text secondary dark/light:
#a8a8a8 / rgba(0,0,0,0.6) - Text muted dark/light:
#666666 / rgba(0,0,0,0.38)
4) Typography
- Font family:
Fourge, then-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif - Base font size:
14px - Header title:
28px,500,line-height: 1.2 - Header subtitle:
13px - Widget title:
11px,500, uppercase,letter-spacing: 0.8px - Section header (new standard):
18px,500, Title Case,letter-spacing: 0.2px,line-height: 1.1 - Body table text:
12px/13pxby column importance
5) Card System
- Default widget shell:
background: var(--card-bg)border: 1px solid var(--border)border-radius: 8pxpadding: 18px 21pxbackdrop-filter: blur(12px)+-webkit-backdrop-filter
- Card width is not an intrinsic card token.
- Width is owned by the page/grid/container the card lives in.
- Cards should fill the slot provided by that page layout unless a page-level rule explicitly fixes a track.
- Compact card radius (legacy generic
.card):4px(do not use for new dashboard widgets)
6) Header + Top Right Controls
- Site header:
padding-top: 24px,padding-bottom: 24px - Right control row:
- Search icon button:
32x32 - Search button to theme toggle space:
7px(search-wrap margin-right) - Theme toggle:
32x32 - Theme toggle to avatar:
14px(avatar-wrap margin-left) - Avatar button:
49x49, circle,2pxinner ring +2pxaccent outline
- Search icon button:
6.5) Section Control Bars (Tabs + Actions)
- For page-level card controls (ex: Tasks/Projects, Finances tabs):
- container uses:
display: flex,align-items: center,gap: 4,margin-bottom: 10,flex-shrink: 0 margin-bottom: 10is the site-wide standard gap between any tab/control bar and the card(s) below — use this everywhere, no exceptions- tab bar row must always be
min-height: var(--btn-height)so the gap to the card never shifts when action buttons appear/disappear - tabs stay on the left in source order
- action buttons group sits on the right using:
margin-left: auto,display: flex,align-items: center,gap: 8 - do not use hardcoded spacer blocks (
widthfiller divs) to force alignment - icon-only filter/action buttons share the same row and align vertically with add buttons
- when the control bar uses a multi-column grid (to align with split-card layouts below), add
align-items: centerto the grid andmin-height: var(--btn-height)to every column so row height is stable across all tab states
- container uses:
7) Dashboard Grids (Team)
- Stat row:
grid-template-columns: 1fr 1fr 1fr 1.5fr,gap: 24,margin-bottom: 0 - Row 2:
grid-template-columns: 1fr 1fr 280px,gap: 24,margin-top: 24- Order: Recent Activity (left), Tasks In Progress (center), Calendar (right, fixed width)
- Row 3:
grid-template-columns: 1fr 1fr,gap: 24,margin-top: 24 - Row 4 full-width:
margin-top: 24
8) Stat Cards
- Card min height:
120px - Internal row gap:
21px - Label/value/sub spacing:
- Label:
margin-bottom: 5px - Value:
30px,400,letter-spacing: -0.5,line-height: 1.1 - Sub:
12px,margin-top: 5px
- Label:
- Icon badge:
27x27, circle - Icon glyph:
13x13
9) Calendar
- Card uses widget shell
- Header-to-grid gap:
14px - Weekday label:
10px,600,letter-spacing: 0.5 - Day cell button:
28x28, circular - Day number:
12px - Today style: bg
#F5A523, text#0d0d0d,700 - Dots: up to 3, each
3x3, gap2 - Popover:
- Anchored left of cell:
right: calc(100% + 8px), vertical centered width: 210px,padding: 10px 12px,border-radius: 8px- shadow
0 12px 32px rgba(0,0,0,0.45) - row dot
6x6, row text12px
- Anchored left of cell:
10) Activity + Performance Rows
- Visible rows target: 5
- Row layout:
display:flex,align-items:center,gap:10px - Row spacing:
margin-top: 10pxfrom second row onward - Name text:
13px - Meta/date text:
11px - Progress track:
height: 4px,radius: 2px - Percentage width slot:
min-width: 28px - Empty states in all cards (dashboard, profile, tasks, projects, file sharing, etc.):
- any
No ...message is centered in the card body (display:flex,align-items:center,justify-content:center) - use shared class treatment (
.card-empty-center) for consistency - avoid top-offset-only placement for empty text
- any
11) Tables
- General table layout in dashboard cards:
table-layout: fixed,border-collapse: collapse - Column widths are table-specific, not universal tokens.
- Widths belong to the individual table/view that defines them.
- Reuse widths only when the same table pattern is intentionally repeated.
- Header cells:
font-size: 10px,font-weight: 500, uppercase,letter-spacing: 0.6px- bottom spacing:
padding-bottom: 12px - sticky behavior for scrollable tables:
- table headers stay fixed while body scrolls
- use shared sticky-head treatment for all app tables (
position: sticky; top: 0)
- table scrollbars are visually hidden for table scroll containers; wheel/trackpad scrolling remains active
- Body cells:
- primary text:
13px - secondary/metrics text:
12px - row vertical spacing via cell padding: typically
5px
- primary text:
- Hot Tasks column widths:
- check
10%, task40%, requested by35%, due by15%
- check
- Client Highlight column widths:
- icon
5%, company22%, contact23%, projects13%, open13%, outstanding12%, paid12%
- icon
- Sorting rule:
- Every visible data column header must be sortable.
- Use clickable header controls (
SortTh) with ascending/descending indicator. - Exclude only non-data utility/action columns (checkbox-only, icon-only status marker, action buttons).
12) Profile Page
- Container: full available content width, column,
gap: 24 - Top row:
grid-template-columns: 60fr 40fr,gap: 24 - At
<=1200px: top row stacks to one column - Main profile card uses widget shell
- Profile card width is determined by the profile page grid, not by the card itself.
- Internal card layout:
- row
gap: 20px - portrait max
140x140, circle - portrait aligns flush in the row without extra wrapper padding/side space
- detail grid
140px 1fr,row-gap: 8,column-gap: 12,margin-top: 14 - profile name/title:
18px,500,line-height: 1.2 - company subtitle:
13px, secondary text - contact/detail rows:
13px, primary text - social row
margin-top: 14,gap: 8 - company line under title is always visible; fallback display is
—when no company is assigned - right-side meta labels (
Member Since,Role) use widget-title sizing:11px,500, uppercase,letter-spacing: 0.8px - right-side meta values use body sizing:
13px - self-only edit button:
position: absolute,top: 18px,right: 21px(aligns to card padding),border-radius: 8px(matches card),height: 22px,font-size: 12px,padding: 0 12px - default outline button hover/focus fills with accent gold and uses dark text
- row
- Right calendar card shows only tasks/events assigned to the viewed profile user
- Profile page left column includes a
Tasks In Progresscard aboveRecent Activity - Profile
Tasks In Progressfollows the dashboard table/card pattern:- same widget shell and header treatment
- same show-all behavior (5 visible by default)
- profile-specific filter is tasks assigned to the viewed user with statuses
in_progress,on_hold, orclient_review - columns are
TaskandStatus
- Profile activity feed action text pattern:
Task started,Task submitted,Task approved,Task rejected(sentence title case) - Activity feed includes:
- actions performed by the viewed user
Task approvedandTask rejectedentries for tasks assigned to that viewed user (even if actioned by someone else)
12.5) File Sharing
- Main split:
grid-template-columns: minmax(0, 1fr) minmax(0, 3fr)for an effective25 / 75layout,gap: 24 - Left stacked cards (
Pinned,Navigation):gap: 24 - Canonical folder structure:
- team and subcontractor profile folders live at
Team/<User Name> - company folders live at
Clients/<Company Name> - project folders live at
Clients/<Company Name>/Projects/<Project Name> - every project auto-creates
00 Project Files - task folders live at
Clients/<Company Name>/Projects/<Project Name>/<Task Name> - every task auto-creates
Request Info,Working Files, andOld Books - request submission files are copied into
Request Info/R00,R01, and higher revision folders - renaming a team user, subcontractor user, company, project, or task must rename the matching filesystem folder
- folder repair/backfill is additive only: create missing folders, leave existing folders untouched
- request-info backfill must skip files that already exist and must not overwrite added working files or user-managed files
- deleting a task in the portal moves its task folder into
Archive/Clients/<Company Name>/Projects/<Project Name>/<Task Name>instead of deleting it - deleting a project in the portal moves its project folder into
Archive/Clients/<Company Name>/Projects/<Project Name>instead of deleting it - orphan reconciliation may move folders into archive when they no longer map to portal records
- orphan team folders move to
Archive/Team/<Folder Name>when no team or subcontractor profile matches that folder name - orphan project folders move to
Archive/Clients/<Company Name>/Projects/<Project Name>when no portal project matches that folder name under that company - orphan reconciliation does not remove company roots or overwrite archived content; it merge-moves into archive
- team and subcontractor profile folders live at
- Access/home rules:
- team members have full file access
- client users with exactly one tied company use that company folder as home:
/Clients/<Company Name> - client users tied to multiple companies use
/Clientsas home and only see company folders they are tied to /Clientsacts as a virtual parent for multi-company client users and must not expose unrelated company folders- subcontractor users use virtual home
/ - subcontractor virtual home shows
Teamplus only the project folders they are assigned to - subcontractor users skip all other root folders and must not see unrelated client or internal roots
- subcontractor
Teammaps to the real sharedTeamfolder - each subcontractor project folder is a virtual root that maps to the real client project path
/Clients/<Company>/Projects/<Project>
- Right card header:
- title label is
File Browser - drag/drop note sits directly under the title and uses subtext sizing:
12px - current file path uses the same
11pxuppercase label treatment as the title row family - toolbar does not show
+ FolderorUploadbuttons - upload happens by dragging and dropping files or folders into the right card
- delete action uses the same geometry as the profile edit button (
top: 18px,right: 21px,8pxradius,22pxheight,12pxtext,0 12pxpadding) but uses danger styling with red hover/fill
- title label is
- Right card table:
- uses dashboard table styling (
table-layout: fixed, transparent header row,10pxuppercase headers,13px/12pxbody text) - card padding matches dashboard widget shell (
18px 21px) - clickable text inside the table highlights in accent; rows do not use block hover fill
- body cells are borderless like dashboard card tables
- body cell padding matches dashboard tables exactly:
5px - visible headers (
Name,Size,Modified) use shared sortable header controls - file/folder icon footprint is compact so row height stays on the same cadence as dashboard tables
- alternating rows use a very subtle theme-aware tint for scanability
- uses dashboard table styling (
- Navigation tree:
Homealigns to the first arrow column- connector line runs through the arrow center column
- pinned folders are user-specific and must not be shared across users on the same device/browser session
- hover is text-only accent color, no row fill
13) Radius + Geometry Rules
- Dashboard/profile widgets:
8pxradius - Sidebar:
8pxradius - Standard app buttons use the
Edit Profilegeometry:- geometry is tokenized globally in CSS vars and must be reused (no per-page hardcoded button geometry):
--btn-height--btn-padding-x--btn-radius--btn-font-size--btn-font-weight--btn-letter-spacing--btn-line-height
- height:
22px - horizontal padding:
0 12px - border-radius:
8px - font-size:
11px - font-weight:
500 - letter-spacing:
0.8px - text-transform: uppercase
- line-height:
1 - label color follows theme text roles:
- dark mode: primary button labels use dark text on accent fill; outline labels use primary text on transparent surface
- light mode: outline labels use light-theme primary text; danger labels use danger text
- outline button border uses the same
var(--border)token as card outlines unless a button role explicitly overrides it - keep existing button role styling intact (
outline,primary,danger); geometry changes, not role colors
- geometry is tokenized globally in CSS vars and must be reused (no per-page hardcoded button geometry):
- Inputs/dropdowns may use their own control radius unless a page-specific rule says otherwise
- Circular elements (avatar/day/icon badges):
50%
14) Z-Index Stack
- Sidebar:
200 - Header dropdowns/tooltips:
300 - Calendar hover popover:
1002within card context (card can be 1001 active) - Modal overlay:
1200
14.5) Loading Popup
- Use shared
PageLoadercomponent for loading overlays instead of page-specific popup implementations. - Loading popups/overlays, including shared site-wide page loaders and file-sharing loading states, use the same widget shell as dashboard cards:
background: var(--popup-bg)border: 1px solid var(--border)border-radius: 8pxpadding: 18px 21pxbackdrop-filter: blur(12px)+-webkit-backdrop-filter
- Popup surface is theme-aware:
- dark mode: deep translucent dark surface
- light mode: bright translucent white surface
- Loading overlay scrim is theme-aware:
- dark mode:
rgba(0,0,0,0.58) - light mode: soft light scrim via
--overlay-scrim
- dark mode:
- Loading title uses widget header treatment:
11px,500, uppercase,letter-spacing: 0.8px, secondary text - Progress track:
- height:
4px - radius:
2px - track color: secondary card tone (
var(--card-bg-2)) - progress fill: accent
- height:
- Progress/meta text under the bar uses subtext sizing (
12px) and secondary text
14.6) Modal Form Fields
- Modal forms that follow
Edit Profilestyling (includingAdd Task/New Task) share the same field typography and theme tokens:- modal surface transparency matches
Edit Profilecard shell (background: var(--card-bg)), not a denser popup-specific override - field labels:
11px,500, uppercase,letter-spacing: 0.8px, colorvar(--text-secondary) - input/select text:
13px, colorvar(--text-primary), left aligned - textarea text:
13px, colorvar(--text-primary) - field surfaces and borders use global theme tokens (
var(--card-bg-2)+var(--border)) for dark/light parity - modal form action buttons use the standard outline geometry/style (
btn btn-outline) unless a page explicitly defines a different role
- modal surface transparency matches
14.7) Status Tags
- Shared status tags use
StatusBadge(.badge.badge-status). - Rule: any workflow/state value (
not_started,in_progress,on_hold,client_review,client_approved,invoiced,paid,active,completed, etc.) must render throughStatusBadgeonly. - Do not use raw
span.badge badge-*for statuses. - Base badge geometry:
display: inline-flexalign-items: centergap: 4pxpadding: 3px 10pxborder-radius: 4pxfont-size: 11pxfont-weight: 400letter-spacing: 0.3pxwhite-space: nowrap
- Status badge geometry override (
.badge-status):display: inline-flexalign-items: centerjustify-content: centermin-width: 78pxheight: 20pxpadding: 0 8pxfont-size: 10pxline-height: 1.05box-sizing: border-boxpadding-top: 1px(optical vertical-centering correction for current font metrics)
- Status colors are variant classes (
.badge-not_started,.badge-in_progress, etc.) and are theme-aware. - Non-status chips (example: invoice line-item type
Initial/Revision, or service-type labels) may use.badgevariants directly. - When a non-status chip should visually align with status pills, add
.badge-statusto match geometry. - Exception: compact urgent tag (
.badge-needs_revision) uses tighter geometry:min-width: 28pxpadding: 3px 4pxborder-radius: 4px- fixed red treatment in both dark/light themes.
15) Motion
- Motion vars:
- fast
160ms - base
220ms - easing
cubic-bezier(0.22, 1, 0.36, 1)
- fast
- Dropdown animation:
ui-fade-upfromtranslateY(4px)+ opacity 0 -> 1
17) Hover Interaction Contract
- Sidebar, header icon buttons, dropdown items, and avatar menu items must show a visible hover surface before click.
- Single hover source-of-truth block controls these elements.
- Dark hover surface baseline:
#1f1f1f. - Light hover surface baseline:
rgba(0,0,0,0.08). - Nav icon opacity must lift from muted to full on hover (
opacity: 1). - Clickable text links use text-only highlight on hover/focus (
color: var(--accent)), not whole-row/background fill. - Hover and active must be visually distinct:
- hover uses stronger temporary contrast (
bg+ thin border), - active remains persistent selected-state background.
- hover uses stronger temporary contrast (
- File Sharing left navigation exception:
Pinned+Navigationtree rows use text-only hover highlight (color: var(--accent)).- Do not apply row background fill for hover in this section.
17.5) Link Interaction Standard
- Use shared link classes only; do not hand-roll page-specific link hover styles:
- table/text links inside cards and tables:
table-link - inline action links/buttons in cards/feeds:
dashboard-inline-link
- table/text links inside cards and tables:
- Hover behavior for both classes:
- color changes to accent gold (
var(--accent)) - no underline on hover/focus
- cursor remains pointer
- color changes to accent gold (
- For table-heavy rows where hit targets are tight, row hover may also promote link color to accent; still keep text-only link treatment (no full-row fill just for links).
- Do not rely on inline style color overrides for hover behavior; if a variant is needed, add/extend a shared class in
index.css.
16) Non-Negotiable Implementation Rules
- Keep gradient backgrounds on
html, notbody - Keep widget shell values (
18px 21px,8px, blur 12, border token) consistent - Maintain global
24pxspacing rhythm for page/frame/grid gaps - Keep team dashboard card order:
- Open Tasks
- Active Projects
- Net Profit
- Revenue (wide)
- Keep row 2 order: Recent Activity (left), Tasks In Progress (center), Calendar (right fixed at
280px)
18) Invoice Rules
- New work (
R00) is billed at the new-service rate. - Revision
R01is free. - Revisions
R02+are billed incrementally: each uninvoiced revision entry contributes exactly1xrevision charge. - Previously invoiced revisions are excluded; later invoices only charge newly uninvoiced revisions (e.g. if
R02was already invoiced, atR04onlyR03+R04are billed).