Skip to content

Latest commit

 

History

History
43 lines (28 loc) · 5.94 KB

File metadata and controls

43 lines (28 loc) · 5.94 KB
name project-mtp-ui-decisions
description Consolidated frontend/UI/CSS/layout decisions for mtp-web — stack, monorepo, design-system, state, fidelity, conventions. Read before any frontend work.
metadata
type
project

All locked frontend decisions in one place. Authoritative ADRs in parentheses; this is the fast index.

Stack (ADR-0007): React 19 + TypeScript only (no JS, strict) + Vite. Radix Primitives (headless @radix-ui/react-*, NOT @radix-ui/themes). CSS Modules + PostCSS + CSS custom properties — compile-time only, no runtime CSS-in-JS (no Emotion/styled-components), no utility framework (no Tailwind), no styled component kits. Native CSS Grid + container queries. TanStack Table + Virtual (headless). lucide-react. Zod + React Hook Form. i18next.

State (ADR-0019): server state = TanStack Query (sole owner, never copied into a store). Client/UI state = Zustand. Forms = React Hook Form + Zod. Shareable view state = URL params. No Redux/RTK. Rule: no server data in Zustand. SignalR pushes into the query cache. render=useQuery, action=useMutation+invalidateQueries; query keys [domain,module,resource,params].

Component authoring (ADR-0008 + [[project-mtp-component-authoring]]): native HTML default (<button>/<input>/<table>/<form>); Radix only for non-trivial behaviour (focus trap, portal, popover, type-ahead, dismiss). Radix wrapped in ui/<Name>/ via asChild + forwardRef, never raw in feature code. Inputs uncontrolled (RHF register). Tables headless — we render the DOM, TanStack owns logic. One .module.css per component; no global classes except tokens. JSX rename rules (className/htmlFor/etc.).

Layout primitives (ADR-0007): in-house Box, Stack, Cluster, Grid, SplitPane, Center, Container, Sidebar — ~30 lines CSS each. No grid/layout library.

Navigation depth (ADR + [[project-mtp-nav-depth]]): 4-level — module (rail) → group (submenu header) → page (item) → view (in-page tab). Collapsible groups, Ctrl-K palette, never 5-deep. Entity-360 hubs add a vtab rail (still level-4).

Mockup shell (ADR-0010): all mockups use the shared _shell.js + _mockup.css + _nav-manifest.js; never inline chrome. Zoho-style 2-stage dark-rail layout.

Design system (ADR-0020): the React UI is a faithful 1:1 PORT of the finalized pure-HTML shell + Zoho theme (_mockup.css/_shell.js) — not a redesign; a rendered React screen must be visually indistinguishable from its mockup. Two-tier tokens: primitive scale → semantic (components use semantic only); light-first, theme-ready (dark/tenant = token remap, no component edits). Build order: tokens → layout primitives → native+CSS components → Radix wrappers → DataGrid → AppShell → composites → pages. Reuse audit first (in-repo only): PORT from _mockup.css/mockups, BUILD only genuine gaps; never re-author an existing class.

Monorepo (ADR-0021): frontend is a pnpm-workspace monorepo (+ Turborepo). packages/{tokens,ui,icons,hooks,config} + apps/web. Internal scope @mtp/*, workspace:* deps. NO npm publishing — every package private:true, internal consumption only. Single Vite bundle (ADR-0006, no module federation); Vite transpiles workspace packages directly. "No monorepo" in ADR-0005 means across stacks (.NET/Node/Flutter separate repos) — the frontend repo is internally a workspace monorepo; not a contradiction.

Hosting: all repos under github.com/paramhq, always private (ADR-0005 amendment). Repos: mtp-docs, mtp-api (.NET), mtp-web (frontend monorepo).

Responsive (ADR-0020): every screen responsive, container-query-first (adapts to container, not just viewport). AppShell collapses rail→icons + submenu→drawer narrow; DataGrid → stacked cards on mobile; multi-col forms → one col; self-service mobile-first. No fixed-pixel layouts; breakpoints are tokens.

Hard exclusions:@gnwebsoft/ui (not used in this project — ignore it). ❌ Redux/RTK. ❌ runtime CSS-in-JS / Tailwind / MUI/Chakra/AntD/Mantine / Radix Themes. ❌ npm publishing. ❌ JS (TypeScript only). ❌ module federation.

Reports/PDF/Excel: native libs (QuestPDF / ClosedXML), never HTML→PDF — see [[feedback-reports-native-libs]].

Build status / specifics (mtp-web exists at D:\Projects\mtp-web):

  • Scaffolded (pnpm + Turbo + Vite + React 19 + TS strict). @mtp/tokens (two-tier), @mtp/ui (Stack/Cluster/Grid/Box, Button/StatusPill/KpiCard/DataTable, AppShell), @mtp/icons (inline set ported from _shell.js). AppShell verified pixel-ditto to mockups/_shell-demo.html.
  • Tree-shaking: sideEffects = ["**/*.css"] for tokens/ui, false for icons/hooks. ESM + barrel exports OK because only CSS is side-effectful → unused component JS is dropped.
  • @/ aliasapps/web/src in BOTH vite.config.ts (resolve.alias) and apps/web/tsconfig.json (paths + baseUrl).
  • Fonts must load in apps/web/index.html (Inter + Fraunces + JetBrains Mono via Google Fonts) — else design breaks (system-font fallback). Same <link> as the mockups.
  • @types/node in apps/web devDeps + "node" in tsconfig types (for vite.config). pnpm onlyBuiltDependencies: ["esbuild"] in root.
  • TS strict extras on: noUncheckedIndexedAccess, verbatimModuleSyntax, isolatedModules.
  • Component library complete (2026-05-22): layout (Box/Stack/Cluster/Grid/Center/Container/SplitPane/Sidebar); Button/IconButton/StatusPill/Badge/Card/KpiCard/EmptyState/Tabs/FilterBar+Chip; Field (Label/TextField/Textarea/FormField); DataTable (hand DOM) + DataGrid (TanStack, sortable); Radix wrappers Dialog/Popover/Tooltip/DropdownMenu/Select/Switch/Toast (Toast = useToast() over Zustand store). All exported from @mtp/ui barrel; typechecks clean. Next page-specific components built as 5b/5c needs them.

Pipeline: see docs/module-prep-prompt.md (Layer 5: identify pages → design HTML → design-system foundations → React pages → endpoints → wire).