Mobile screens in the Fuuz Application Designer must prioritize simplicity, clarity, and task completion. The design standard is built on three core principles:
The standard template uses a four-level container architecture:
ROOT (Screen)
└── ContainerOuter (Prevents uncontrolled growth)
├── ContainerHeader (Fixed height: 60px)
├── ContainerBody (Flexible, scrollable)
│ └── Form1
│ └── ContainerInnerBody (Content wrapper)
└── ContainerFooter (Fixed height: 80px)
Purpose: Primary layout constraint container
Properties:
width: 100%, height: 100%flexDirection: columnjustifyContent: space-betweenoverflow-x: hidden, overflow-y: hiddenflexGrow: false, flexShrink: falseWhy: Prevents the screen from growing uncontrollably in both X and Y axes. This is the foundation of layout stability.
Purpose: Fixed navigation and title area
Properties:
height: 60px (fixed)width: 100%flexDirection: rowjustifyContent: space-betweenalignItems: centerbackground: custom (#5b30df or brand color)padding: 4Content:
Developer Actions:
Purpose: Main scrollable content area
Properties:
height: 0px (allows flex-grow to control size)width: 100%flexGrow: trueflexShrink: falseoverflow-y: autooverflow-x: hiddenscrollbar-width: thinWhy: Setting height to 0px with flexGrow:true allows the body to fill available space between header and footer without causing overflow issues.
Purpose: Content padding and organization wrapper
Properties:
height: 0pxwidth: 100%flexGrow: trueflexDirection: columnpadding: 6 (24px equivalent)Content: Form inputs, data displays, action elements
Purpose: Fixed action button area
Properties:
height: 80px (fixed)width: 100%flexDirection: rowjustifyContent: space-betweenalignItems: stretchContent: Primary and secondary action buttons (Submit, Clear, Cancel, etc.)
When building screens using responsive layouts, follow these thought processes:
Think: "What needs to be fixed, and what needs to be flexible?"
Process:
Think: "Where should expansion occur, and where must it stop?"
Rules:
flexGrow: false on containers that must maintain sizeflexGrow: true only on content areas that should fill available spaceheight: 0px with flexGrow: true to prevent initial overflowflexShrink: false on fixed-height elementsThink: "What happens when content exceeds container size?"
Guidelines:
overflow: hidden (prevent page scroll)overflow-y: auto (enable content scroll)Think: "How should child elements flow?"
Pattern:
ContainerOuter → column (vertical stacking)
├── ContainerHeader → row (title left, menu right)
├── ContainerBody → column (inputs stack vertically)
└── ContainerFooter → row (buttons side-by-side)
Think: "How should available space be distributed?"
Common Patterns:
justifyContent: space-between - Maximum separation (headers, footers)justifyContent: flex-start - Top/left alignment (content areas)alignItems: stretch - Fill cross-axis (button groups)alignItems: center - Center cross-axis (headers)All form inputs must include:
{
"height": "120px",
"width": "100%",
"variant": "outlined",
"dataFontSize": 24,
"labelFontSize": 15,
"alignItems": "start"
}
Why Fixed Height?
Setting height: 120px prevents screen flexing when validation messages appear. This maintains a stable layout and prevents disorienting visual shifts.
Developer Responsibility: Choose appropriate input types based on data requirements
| Use Case | Input Type | Properties |
|---|---|---|
| Short text (SKU, ID) | TextInput | type: "text" |
| Numbers only | TextInput | type: "number" |
| Email addresses | TextInput | type: "email" |
| Phone numbers | TextInput | type: "tel" |
| Search/scan codes | TextInput with Scan | type: "text" + scan button |
| Selection from list | SelectInput | dialogMode: "never", optionLimit: 5 |
| Yes/No, True/False | SwitchInput | Simple toggle |
| Date selection | DateInput | Native date picker |
| Time selection | TimeInput | Native time picker |
Standard Configuration:
{
"type": "text",
"height": "120px",
"width": "100%",
"variant": "outlined",
"dataFontSize": 24,
"labelFontSize": 15,
"validation": { "required": true },
"label": "Clear, Descriptive Label"
}
Label Requirements:
Description Field: Use the description property to document:
Standard Configuration:
{
"type": "select",
"height": "120px",
"width": "100%",
"variant": "outlined",
"dataFontSize": 24,
"labelFontSize": 15,
"dialogMode": "never",
"optionLimit": 5,
"isClearable": true,
"searchPredicate": "contains"
}
Critical: Always use predicate filters to return only active/usable options
Filter Pattern:
{
"additionalFilter": {
"_and": [
{
"active": { "_eq": true }
}
]
}
}Why: Presenting inactive or unusable records to mobile users causes confusion and support issues.
For inputs requiring barcode/QR scanning:
Buttons in ContainerFooter should fill available space:
Standard Pattern: Two-button footer
{
"width": "50%",
"height": "100%",
"margin": 4,
"customTextSize": 24
}
Color Coding:
Use JSONata expressions to control button states:
Submit Button Pattern:
$isNilOrEmpty($components.Form1.formState.values.field1)
or
$isNilOrEmpty($components.Form1.formState.values.field2)
Clear/Cancel Button Pattern:
$isNilOrEmpty($components.Form1.data.field1)
and
$isNilOrEmpty($components.Form1.data.field2)
and
$isNilOrEmpty($components.Form1.data.field3)
Why Different?
Required Menu Structure:
Header Menu Placement:
Navigation Flow:
Home (hub)
├── Function Page 1 (with menu → Home)
├── Function Page 2 (with menu → Home)
└── Function Page 3 (with menu → Home)
Why: Mobile users need clear escape routes. A consistent Home hub provides orientation and reduces support calls.
Developer Responsibility: Customize validation messages for clarity
Default vs Custom:
Message Guidelines:
Examples:
| Field Type | Good Message | Bad Message |
|---|---|---|
| Required field | "Enter employee ID to continue" | "Field required" |
| Invalid format | "Use format: 123-456-7890" | "Invalid input" |
| Selection | "Choose a destination location" | "Selection required" |
| Duplicate | "This number is already used" | "Duplicate entry" |
Use this checklist for every mobile screen:
overflow: hidden, flexGrow: falseheight: 0px, flexGrow: true, overflow-y: autovariant: "outlined"dataFontSize: 24, labelFontSize: 15 on all inputsvalidation.required: truecustomTextSize: 24)Guideline: Avoid adding more than 4 inputs to a single mobile screen
Why:
When You Need More Than 4 Inputs:
Example Pattern:
Screen 1: Item Selection (2 inputs)
↓
Screen 2: Location Selection (2 inputs)
↓
Screen 3: Quantity & Notes (2 inputs)
↓
Review & Submit
Guideline: Avoid adding more than 2 primary actions per screen
Standard Pattern:
Why:
Exceptions:
Use when: Related inputs depend on prior selections
Pattern:
hidden property with JSONata conditionalsExample:
// Hide quantity input until item selected
hidden: $isNilOrEmpty($components.Form1.formState.values.item.id)
Use when: Most users select the same option
Implementation:
dataPath with initial valueExample: Default to current shift, current location, current user
Guidelines:
optionLimit: 5 for initial loadsearchPredicate: "contains" for filteringorderByField for predictable sortingWhy: Mobile networks can be slow; limiting options improves responsiveness
Guidelines:
autoLoad: false if data isn't needed immediatelyRequirements:
Implementation:
height: "100%" (fills 80px footer)height: "120px" (plenty of touch area)Standards:
Why: Prevents zooming, reduces errors, accommodates various vision levels
Requirements:
Testing: Test header text on brand color backgrounds
Use Case: Collect 2-3 pieces of information and submit
Structure:
ContainerHeader (Screen Title + Menu)
ContainerBody
└── Form
├── Input 1 (required)
├── Input 2 (required)
└── Input 3 (optional)
ContainerFooter
├── Submit Button (50%)
└── Clear Button (50%)
Key Points:
Use Case: Scan barcode, display related data, collect additional input
Structure:
ContainerHeader (Screen Title + Menu)
ContainerBody
└── Form
├── Scan Input (with button)
├── Display Data (read-only text, loaded after scan)
├── Additional Input 1
└── Additional Input 2
ContainerFooter
├── Submit Button (50%)
└── Cancel Button (50%)
Key Points:
Use Case: Choose from list, review selection, confirm action
Structure:
Screen 1: Selection
ContainerHeader (Screen Title + Menu)
ContainerBody
└── Form
├── SelectInput (main selection)
└── Optional filter inputs
ContainerFooter
├── Continue Button (50%)
└── Cancel Button (50%)
Screen 2: Confirmation
ContainerHeader (Screen Title + Menu)
ContainerBody
└── Display selected data (read-only)
ContainerFooter
├── Confirm Button (50%)
└── Back Button (50%)
Key Points:
Use Case: Complex task requiring multiple inputs in sequence
Structure:
Each Step:
ContainerHeader (Screen Title + Step Indicator + Menu)
ContainerBody
└── Form
├── Step-specific inputs (max 3)
└── Optional help text
ContainerFooter
├── Next/Submit Button (50%)
└── Back/Cancel Button (50%)
Key Points:
Use Case: Primary navigation point with status information
Structure:
ContainerHeader (App Title + Menu)
ContainerBody
└── Container (card-style layout)
├── Status Display Cards
└── Action Buttons (navigate to functions)
ContainerFooter
└── Optional refresh or global action
Key Points:
Symptoms:
Causes:
overflow-x: hiddenSolutions:
overflow-x: hiddenwidth: 100% or appropriate percentageSymptoms:
Causes:
flexGrow: true on ContainerBodySolutions:
height: 0px and flexGrow: truejustifyContent: space-betweenoverflow-y: autoSymptoms:
Causes:
auto instead of fixed heightSolutions:
height: 120px (or appropriate fixed height)height: auto on form inputs in mobile layoutsSymptoms:
Causes:
overflow-y: autooverflow-y: auto (wrong place)Solutions:
overflow-y: autooverflow-y: hiddenheight: 0px and flexGrow: trueSymptoms:
Causes:
and instead of orSolutions:
formState.values not just data$components.Form1.formState.values.dataPathor between required field checks (disabled if ANY empty)Symptoms:
Causes:
Solutions:
Symptoms:
Causes:
Solutions:
Symptoms:
Causes:
additionalFilter on SelectInputSolutions:
additionalFilter with active status check_eq: true for boolean active fieldsDocument Control
Version: 1.0