medOS ultra

Frontend Design Kit Style Guide

Style guide for the frontend design kit.

13 min readUpdated 2026-04-16docs/frontend/ULTIMATE_FRONTEND_DESIGN_KIT_STYLE_GUIDE.md

Purpose

This guide documents the visual and composition patterns already used by the HIS frontend design-kit layer and the following reference modules:

  • apps/ever/ui/web/src/modules/medical-record/future-order-checked-in/
  • apps/ever/ui/web/src/modules/medical-record/future-order-wait-check-in/
  • apps/ever/ui/web/src/modules/medical-record/medical-record-table/
  • apps/ever/ui/web/src/modules/medical-record/vital-sign/
  • apps/ever/ui/web/src/modules/list-patient/visit/DialogAppointment.tsx
  • apps/ever/ui/web/src/modules/list-patient/visit/DialogCreateVisit.tsx
  • apps/ever/ui/web/src/modules/list-patient/visit/DialogDocument.tsx
  • apps/ever/ui/web/src/modules/list-patient/visit/DialogNhsoAgent.tsx
  • apps/ever/ui/web/src/modules/list-patient/visit/DialogNhsoWebService.tsx
  • apps/ever/ui/web/src/modules/list-patient/visit/DialogViewHistory.tsx
  • apps/ever/ui/web/src/modules/screening-patient/dialog-screening/

The goal is to keep new UI work visually consistent with the shared wrapper components under:

  • src/common/components/mui/
  • src/common/components/custom/

Core Principle

Build UI with the shared design-kit wrappers first, and only drop to raw MUI when the design-kit does not already provide the behavior or styling you need.

Prefer:

  • @components/mui/button
  • @components/mui/typography
  • @components/mui/text-field
  • @components/mui/select
  • @components/mui/chip
  • @components/custom/dialog-form
  • @components/custom/dialog-info-header
  • @components/custom/table

Design Language Summary

The referenced screens consistently use the following visual language:

  • Rounded surfaces: primary containers and dialogs use 8px or 16px radii.
  • Soft neutral framing: headers use light gray backgrounds, bordered inputs use soft gray outlines, and tables use subtle dividers.
  • Compact healthcare density: data-heavy screens keep row height tight, but preserve readable padding.
  • Strong sectioning: dialogs are broken into clear blocks using dividers, cards, boxed sections, and spacing.
  • Single accent color: primary actions and interactive emphasis use the green primary tone.
  • Semantic status colors: chips, warnings, and special states should use the shared semantic templates rather than ad hoc colors.

Foundation Components

Dialogs

DialogForm

Use DialogForm as the default modal shell for standard dialogs.

Observed style characteristics:

  • dialog paper uses border-radius: 16px
  • title bar uses a light gray background
  • title content is padded with approximately 18px 24px
  • footer actions typically use 24px padding
  • content commonly uses DialogContent inside the wrapper

Use DialogForm for:

  • visit registration dialogs
  • coverage/history dialogs
  • document preview dialogs
  • NHSO dialogs
  • table-based modal flows

Preferred composition:

  1. DialogForm
  2. patient header or title section
  3. divider
  4. main content blocks
  5. custom footer with action buttons

DialogWithInfo

Use DialogWithInfo for large workflows that need a patient information panel and configurable content area, such as:

  • screening
  • future order workflows
  • multi-step clinical dialogs

Observed behavior:

  • wraps DialogForm
  • adds a patient information header/panel
  • defaults inner content padding to 24px
  • supports top/left/right layout modes
  • is used for high-context clinical workflows

Important note:

  • DialogWithInfo is marked deprecated in code and recommends DialogMaster
  • for legacy-consistent screens, follow the current visual treatment
  • for new large workflows, verify whether DialogMaster is now the preferred shell before introducing another DialogWithInfo

Dialog Layout Rules

Across the referenced dialogs, the common structure is:

  • Header: title and close action handled by DialogForm
  • Patient context: HeaderPatientInformation or DialogInfoPatient
  • Divider: placed immediately after patient context
  • Body: section blocks with 24px internal padding or card-like boxed sections
  • Footer: bottom action row with 24px padding and a top border or divider

Recommended footer patterns:

  • Single dismiss action: right-aligned outlined button
  • Cancel + confirm: outlined secondary action, contained primary action
  • Destructive + standard actions: destructive action left, cancel/save group right
  • Long forms: keep footer fixed within dialog shell rather than floating inside content

Spacing System

The referenced modules repeatedly use these spacing values:

  • 4px for tight inline gaps
  • 8px for compact internal spacing
  • 10px to 12px for small action separation
  • 16px for section inset and first-column table padding
  • 24px for dialog padding, card padding, and footer padding
  • 30px or 32px for larger section breaks

Recommended usage:

  • Dialog shell content: 24px
  • Section-to-section gap inside dialog: 24px to 32px
  • Inline button gap: 8px to 10px
  • Compact field group gap: 8px to 16px
  • Table first column padding: 16px

Border Radius

Use the existing radius hierarchy:

  • 16px
    • dialog shells
    • tab tops
    • large cards/boxed sections
  • 8px
    • buttons
    • text inputs
    • selects
    • compact cards
    • table wrappers
    • vital sign cells
  • 4px
    • standard chips

Typography

Always use the shared Typography wrapper when possible.

Common altColor values seen in the referenced modules:

  • textBlackMain
  • textBlackDark
  • textBlackLight
  • textBlackDisable
  • primaryMain

Typical roles:

  • Dialog title: variant="h8"
  • Section title: variant="h6", h8, or h9
  • Field label / table header: variant="inputLabel"
  • Regular content: body1, body2, or default text
  • Dense tab label: h9

Rules:

  • use uppercase sparingly for compact metadata headings and table labels
  • prefer wrapper-driven color tokens instead of hardcoded hex values
  • if text is translated, use the i18n prop or surrounding translation pattern consistently

Buttons

Use the shared Button wrapper.

Observed styling:

  • default button radius is 8px
  • rounded creates pill-style actions for emphasis in some flows
  • loading state is built in
  • contained buttons are the primary commit action
  • outlined buttons are the default secondary action

Recommended usage:

  • Primary action: variant="contained"
  • Secondary action: variant="outlined"
  • Neutral dismiss: outlined
  • Add row / add item: outlined or contained with startIcon
  • Dialog footer: keep no more than one dominant contained action

Form Grid Layout

Use MUI v7 Grid2 (import { Grid } from '@mui/material') with size={{ xs: 12, sm: N }} responsive breakpoints. Do not use the old Grid1 pattern (item xs={N} sm={N}) or mix item with size.

Never stretch short inputs to full width

When a field’s expected value is short (a few characters or a short list), it must not fill the entire dialog width. Expanding a 2-character dropdown to 100% width wastes space and makes the form harder to scan clinically.

Examples of short inputs that must share a row:

Field Max value length Recommended column span
กลุ่มเลือด (A / B / AB / O) 2–3 chars sm={2} (2 of 12)
Rh (+/-) display text sm={2}
Unit Number short code sm={2}
BP systolic / diastolic 3 digits each sm={2} total (nested 5/2/5)
PULSE 3 digits sm={2}
TEMP (ปกติ / สูง / ต่ำ) 3 options sm={2}
ครั้งที่บริจาค small number sm={2}
น.น. (weight) kg value sm={2}

Correct MUI v7 Grid2 syntax:

<Grid container spacing={2}>
  {/* Short fields — 6 per row at sm+ */}
  <Grid size={{ xs: 12, sm: 2 }}>
    <Autocomplete label="กลุ่มเลือด" ... />
  </Grid>
  <Grid size={{ xs: 12, sm: 2 }}>
    <Autocomplete label="RH" ... />
  </Grid>

  {/* Medium fields — 2 per row at sm+ */}
  <Grid size={{ xs: 12, sm: 6 }}>
    <TextField label="COPPER SULFATE" ... />
  </Grid>
  <Grid size={{ xs: 12, sm: 6 }}>
    <TextField label="ปริมาณเลือด" ... />
  </Grid>
</Grid>

Wrong (do not use):

// Wrong: old Grid1 API — does not work in MUI v7
<Grid item xs={12} sm={2}>...</Grid>

// Wrong: size={12} overrides responsive breakpoints, forces full width
<Grid item size={12} sm={2}>...</Grid>

// Wrong: stretching short input to full width
<Grid size={{ xs: 12 }}>
  <Autocomplete label="กลุ่มเลือด" ... />  {/* A/B/AB/O — does not need 100% width */}
</Grid>

Date + time always paired on the same row

Date fields and their paired time fields must always share a row:

<Grid size={{ xs: 12, sm: 2 }}>
  <InputDatePicker label="วันที่บริจาค" ... />
</Grid>
<Grid size={{ xs: 12, sm: 2 }}>
  <InputTimePicker label="เวลาให้เลือด" ... />
</Grid>

Table action columns

Use isSticky: true on the ITableColumn definition for action columns so they remain fixed to the right side when the table scrolls horizontally. The DynamicTable RowRenderer and HeaderCellRenderer will automatically use ActionCell (sticky, right: 0, z-index 1, shadow) when isSticky is set.

Text Inputs

Use the shared TextField wrapper.

Observed wrapper behavior:

  • label font size 12px
  • label weight 700
  • input border radius 8px
  • standard input padding around 10px 12px
  • supports read-only and number restrictions
  • supports alternate disabled treatment via altDisabledColor

Rules:

  • labels should sit above the field, not inside as the primary identifier
  • keep multiline note fields visually aligned with standard form fields
  • for dense workflows, stay consistent with existing label-weight and border styling

Select Inputs

Use the shared Select wrapper.

Observed wrapper behavior:

  • 8px border radius
  • 24px top margin from label to control
  • outlined border with hover/focus state switching to primary color
  • label matches text-field label treatment
  • supports placeholders, chips rendering, multi-select, clear action, and disabled alt styling

Rules:

  • use fieldNameRender and fieldValue consistently
  • when options are complex, provide a display string such as code - name
  • use chip rendering only when selection state benefits from visual tokenization

Chips and Status Badges

Use the shared Chip wrapper and template values from the design-kit.

Observed styling:

  • default chip radius 4px
  • bold label text
  • semantic text/background/border derived from template

Common use cases in the referenced screens:

  • status pills in future orders
  • compact count indicators
  • ESI or priority-style selection summaries
  • selected values inside multi-select controls

Rules:

  • use template-based semantic colors instead of hardcoded badge colors
  • keep chips concise
  • use rounded count chips only for small numeric summaries

Tabs

Use existing styled tabs where available, such as CustomTabsMain and CustomTabMain.

Observed style characteristics:

  • tab container may sit on a gray background strip
  • inactive tabs use soft gray backgrounds
  • selected tabs switch to white with stronger weight
  • top corners are rounded at 16px
  • indicator can be hidden when the design relies on surface change instead

Tables

Use @components/custom/table for standard tables and keep custom table implementations visually aligned with it.

Shared table treatment:

  • sticky header support
  • white header background
  • header bottom border
  • header padding around 16px 8px, with first column often 16px
  • body rows use alternating striping
  • hover state uses a pale green tint
  • compact body row height around 30px
  • outer border and radius are optional depending on context

Table design rules:

  • use Typography variant="inputLabel" for header labels
  • keep first column padding wider than others
  • prefer alternating row shading for scanability
  • support no-data and loading states inside the table body
  • use expand rows only when secondary detail truly belongs to the record
  • keep row actions compact and aligned to the right

Section Cards and Boxed Panels

The reference dialogs often wrap major form sections in elevated boxes.

Observed pattern:

  • white background
  • 16px radius
  • 24px padding
  • subtle gray shadow
  • section heading aligned left
  • top-right action cluster for add/manage actions

Use this pattern for:

  • treatment rights section
  • large grouped form blocks
  • read-mode detail panels
  • multi-field information groups inside a larger dialog

Patient Information Blocks

Patient context is always visually prominent and appears near the top of the dialog.

Patterns seen:

  • HeaderPatientInformation directly beneath the dialog title
  • DialogInfoPatient as a richer info panel for workflow dialogs
  • a divider immediately below the patient summary

Rules:

  • do not bury patient identity below other content
  • keep patient context visible before the first editable clinical or administrative section
  • use a divider to clearly separate identity from task content

Forms in Read Mode vs Edit Mode

The reference modules consistently distinguish between read mode and edit mode.

Read mode

  • present grouped information in boxed or card sections
  • use label/value pairs
  • reduce input chrome
  • preserve section ordering from the edit view

Edit mode

  • use wrapper controls
  • maintain the same section titles and layout structure
  • allow add/remove actions near the section title area
  • keep action density manageable with clear primary/secondary emphasis

Vital Sign Pattern

The vital-sign UI establishes a compact clinical measurement pattern.

Observed style characteristics:

  • individual vital sign boxes use 8px radius
  • focused state changes background to primary light and border to primary main
  • focused state also changes typography color to primary main
  • read-mode panels use soft gray backgrounds and compact internal padding

Rules:

  • keep vital measurement widgets dense but clearly focusable
  • use visual state change on focus rather than heavy decoration at rest
  • group clinically related values tightly
  • preserve consistent box height across comparable measurements

Loading and Empty States

Use shared wrappers where available:

  • LoadingContent
  • NoDataContent

Rules:

  • loading states should occupy the content area they replace
  • in dialogs, center major loaders when content is not yet available
  • in tables, render loading and empty states inside the table body rather than outside the table shell

Standard Administrative Dialog

Use for:

  • view history
  • document display
  • NHSO check dialogs

Structure:

  1. DialogForm
  2. patient header
  3. divider
  4. one or more boxed sections or content blocks
  5. footer with outlined and contained actions

Workflow Dialog With Patient Context

Use for:

  • screening
  • future order handling
  • large nurse/doctor task flows

Structure:

  1. DialogWithInfo or current approved successor
  2. patient info panel enabled
  3. padded scrollable content column
  4. persistent footer actions

Data-Dense Table Screen

Use for:

  • medical record worklists
  • history tables
  • waiting/check-in lists

Structure:

  1. filter and action bar
  2. shared table wrapper
  3. compact row actions
  4. optional right-side detail or popper actions
  5. pagination below or integrated with the layout

Do and Don’t

Do

  • use design-kit wrappers first
  • keep dialogs rounded and sectioned
  • use 24px as the default dialog and section padding anchor
  • use semantic chip templates
  • preserve patient context at the top of workflow dialogs
  • align table headers and body cells to the established padding rhythm
  • use read mode and edit mode as visual siblings

Don’t

  • build new dialogs directly on raw MUI without checking DialogForm and DialogWithInfo
  • hardcode status colors when a chip template exists
  • mix random border radii on the same screen
  • overuse contained buttons in one action cluster
  • use inconsistent field labeling patterns
  • create bespoke table styling that breaks the established hover, stripe, and header treatment

Implementation Checklist For New Screens

Before merging a new screen or dialog, check:

  • Is there a shared design-kit wrapper for every major primitive used?
  • Does the dialog use 16px shell rounding and 24px footer/content rhythm?
  • Are labels using the same visual weight as existing fields?
  • Are table rows, headers, hover, and striping consistent with the shared table?
  • Are semantic statuses represented with shared chip templates?
  • Is patient context placed at the top when the workflow is patient-specific?
  • Does the screen look visually compatible with DialogCreateVisit, DialogViewHistory, DialogScreening, and the medical record modules?

Reference Files

Shared wrappers

  • apps/ever/ui/web/src/common/components/custom/dialog-form/DialogForm.tsx
  • apps/ever/ui/web/src/common/components/custom/dialog-form/style.ts
  • apps/ever/ui/web/src/common/components/custom/dialog-info-header/DialogWithInfo.tsx
  • apps/ever/ui/web/src/common/components/custom/table/Table.tsx
  • apps/ever/ui/web/src/common/components/custom/table/style.ts
  • apps/ever/ui/web/src/common/components/mui/button/Button.tsx
  • apps/ever/ui/web/src/common/components/mui/button/style.ts
  • apps/ever/ui/web/src/common/components/mui/text-field/TextField.tsx
  • apps/ever/ui/web/src/common/components/mui/text-field/style.ts
  • apps/ever/ui/web/src/common/components/mui/select/Select.tsx
  • apps/ever/ui/web/src/common/components/mui/select/styles.ts
  • apps/ever/ui/web/src/common/components/mui/typography/Typography.tsx
  • apps/ever/ui/web/src/common/components/mui/chip/Chip.tsx
  • apps/ever/ui/web/src/common/components/mui/chip/style.ts

Reference feature modules

  • apps/ever/ui/web/src/modules/list-patient/visit/DialogCreateVisit.tsx
  • apps/ever/ui/web/src/modules/list-patient/visit/DialogAppointment.tsx
  • apps/ever/ui/web/src/modules/list-patient/visit/DialogDocument.tsx
  • apps/ever/ui/web/src/modules/list-patient/visit/DialogNhsoAgent.tsx
  • apps/ever/ui/web/src/modules/list-patient/visit/DialogNhsoWebService.tsx
  • apps/ever/ui/web/src/modules/list-patient/visit/DialogViewHistory.tsx
  • apps/ever/ui/web/src/modules/medical-record/future-order-wait-check-in/FutureOrder.tsx
  • apps/ever/ui/web/src/modules/medical-record/future-order-checked-in/FutureOrderCheckedIn.tsx
  • apps/ever/ui/web/src/modules/medical-record/medical-record-table/MedicalRecordTable.tsx
  • apps/ever/ui/web/src/modules/medical-record/vital-sign/VitalSign.tsx
  • apps/ever/ui/web/src/modules/medical-record/vital-sign/style.ts
  • apps/ever/ui/web/src/modules/screening-patient/dialog-screening/DialogScreening.tsx

Scope Note

This guide is intentionally grounded in the current codebase. When the shared design-kit evolves, update this document to reflect the wrappers and patterns that are actually in production use.

Ask Anything