agentleFS
javiarmestojaviarmesto/ALDC-AL-Development-Collection/skills/skill-pages/SKILL.md

skill-pages

Skill115 starsChanged 10 days agoMITView on GitHub

AL page design patterns for Business Central. Use when creating Card, List, ListPart, Document pages, implementing page extensions, or designing UX layouts.

The file

We split this skill into the rules it gives an agent

10 rules. About 0 are common, 4 are distinctive, and 6 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈4

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 6

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Use UsageCategory = Lists for search/Tell Me discoverability

    line 191
  2. Very specific:

    Add FactBoxes (area(FactBoxes)) for contextual info

    line 394
  3. Distinctive:

    Add Notes and Links system parts

    line 395
  4. Very specific:

    Add RunObject / RunPageLink for navigation actions

    line 403
  5. Very specific:

    Update expressions in OnAfterGetRecord trigger

    line 410
  6. Distinctive:

    Verify layout, navigation, and field behavior

    line 416
  7. Distinctive:

    Test dynamic visibility and styling with different record states

    line 417
  8. Very specific:

    Do NOT modify standard BC pages directly — use pageextension objects only

    line 431
  9. Distinctive:

    Do NOT place more than 3-4 FlowFields on List pages (performance impact)

    line 432
  10. Very specific:

    Do NOT skip ApplicationArea on any field or action

    line 433

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Skill: AL Page Design

Purpose

Design and customize Business Central pages: Card, List, Document, Worksheet, and RoleCenter page types, page extensions with addafter/addbefore/modify, dynamic UI (visibility, styling), FastTab layout, promoted actions, and FactBoxes.

When to Load

This skill should be loaded when:

  • Creating a new page (any type) for a custom or extended table
  • Extending a standard BC page with custom fields, groups, or actions
  • Designing a Document page with header-lines structure
  • Implementing dynamic visibility, conditional styling, or field importance
  • Optimizing page performance (FlowField limits, lazy loading)

Core Patterns

Pattern 1: Card Page

Single-record display with FastTab groups:

page 50100 "Contoso Project Card"
{
    PageType = Card;
    SourceTable = "Contoso Project";
    Caption = 'Project Card';
    UsageCategory = None;          // accessed from list, not search

    layout
    {
        area(Content)
        {
            group(General)
            {
                Caption = 'General';
                field("No."; Rec."No.")
                {
                    ApplicationArea = All;
                    Importance = Promoted;      // visible in collapsed FastTab
                }
                field(Description; Rec.Description)
                {
                    ApplicationArea = All;
                    Importance = Promoted;
                }
                field(Status; Rec.Status)
                {
                    ApplicationArea = All;
                    StyleExpr = StatusStyleExpr;
                }
                field("Starting Date"; Rec."Starting Date")
                {
                    ApplicationArea = All;
                }
            }
            group(Details)
            {
                Caption = 'Details';
                Visible = DetailsVisible;       // dynamic visibility

                field("Project Manager"; Rec."Project Manager")
                {
                    ApplicationArea = All;
                }
                field(Budget; Rec.Budget)
                {
                    ApplicationArea = All;
                }
            }
        }
        area(FactBoxes)
        {
            systempart(Notes; Notes) { ApplicationArea = All; }
            systempart(Links; Links) { ApplicationArea = All; }
        }
    }

    actions
    {
        area(Processing)
        {
            action(Release)
            {
                ApplicationArea = All;
                Caption = 'Release';
                Image = ReleaseDoc;
                Promoted = true;
                PromotedCategory = Process;
                PromotedIsBig = true;

                trigger OnAction()
                begin
                    Rec.TestField(Status, Rec.Status::Open);
                    Rec.Validate(Status, Rec.Status::Released);
                    Rec.Modify(true);
                end;
            }
        }
        area(Navigation)
        {
            action(Entries)
            {
                ApplicationArea = All;
                Caption = 'Ledger Entries';
                Image = LedgerEntries;
                RunObject = page "Contoso Project Entries";
                RunPageLink = "Project No." = field("No.");
            }
        }
    }

    var
        StatusStyleExpr: Text;
        DetailsVisible: Boolean;

    trigger OnAfterGetRecord()
    begin
        StatusStyleExpr := GetStatusStyle();
        DetailsVisible := Rec.Status <> Rec.Status::Open;
    end;

    local procedure GetStatusStyle(): Text
    begin
        case Rec.Status of
            Rec.Status::Open:
                exit('Standard');
            Rec.Status::Released:
                exit('Favorable');
            Rec.Status::Closed:
                exit('Subordinate');
        end;
    end;
}

Pattern 2: List Page

Multi-record display with filters and navigation:

page 50101 "Contoso Project List"
{
    PageType = List;
    SourceTable = "Contoso Project";
    Caption = 'Projects';
    UsageCategory = Lists;
    ApplicationArea = All;
    CardPageId = "Contoso Project Card";
    Editable = false;

    layout
    {
        area(Content)
        {
            repeater(Group)
            {
                field("No."; Rec."No.") { ApplicationArea = All; }
                field(Description; Rec.Description) { ApplicationArea = All; }
                field(Status; Rec.Status)
                {
                    ApplicationArea = All;
                    StyleExpr = StatusStyleExpr;
                }
                field("Starting Date"; Rec."Starting Date") { ApplicationArea = All; }
                // Limit FlowFields on list pages — max 3-4 for performance
                field("Total Budget"; Rec."Total Budget") { ApplicationArea = All; }
            }
        }
    }

    var
        StatusStyleExpr: Text;

    trigger OnAfterGetRecord()
    begin
        StatusStyleExpr := GetStatusStyle();
    end;
}

List page rules:

  • Set Editable = false — use Card for editing
  • Set CardPageId for drill-down navigation
  • Limit FlowFields to 3-4 maximum (each triggers a query per row)
  • Use UsageCategory = Lists for search/Tell Me discoverability

Pattern 3: Document Page (Header + Lines)

page 50102 "Contoso Sales Document"
{
    PageType = Document;
    SourceTable = "Contoso Document Header";
    Caption = 'Sales Document';

    layout
    {
        area(Content)
        {
            group(General)
            {
                Caption = 'General';
                field("No."; Rec."No.") { ApplicationArea = All; }
                field("Customer No."; Rec."Customer No.")
                {
                    ApplicationArea = All;
                    Importance = Promoted;

                    trigger OnValidate()
                    begin
                        CurrPage.Update();      // refresh lines after customer change
                    end;
                }
                field("Document Date"; Rec."Document Date") { ApplicationArea = All; }
            }
            part(Lines; "Contoso Document Subpage")
            {
                ApplicationArea = All;
                SubPageLink = "Document No." = field("No.");
                UpdatePropagation = Both;       // sync header ↔ lines
            }
        }
    }
}
// Lines subpage
page 50103 "Contoso Document Subpage"
{
    PageType = ListPart;
    SourceTable = "Contoso Document Line";
    Caption = 'Lines';
    AutoSplitKey = true;            // auto-increment Line No.

    layout
    {
        area(Content)
        {
            repeater(Lines)
            {
                field(Type; Rec.Type) { ApplicationArea = All; }
                field("No."; Rec."No.") { ApplicationArea = All; }
                field(Description; Rec.Description) { ApplicationArea = All; }
                field(Quantity; Rec.Quantity) { ApplicationArea = All; }
                field("Unit Price"; Rec."Unit Price") { ApplicationArea = All; }
                field("Line Amount"; Rec."Line Amount")
                {
                    ApplicationArea = All;
                    Editable = false;
                }
            }
        }
    }
}

Pattern 4: Page Extension

Extend standard BC pages without modifying them:

pageextension 50100 "Customer Card Ext" extends "Customer Card"
{
    layout
    {
        addafter(General)
        {
            group(ContosoInfo)
            {
                Caption = 'Contoso Information';

                field("Contoso Loyalty Points"; Rec."Contoso Loyalty Points")
                {
                    ApplicationArea = All;
                    ToolTip = 'Total loyalty points for this customer.';
                }
            }
        }
        modify(Name)
        {
            // Change properties of existing fields
            StyleExpr = NameStyleExpr;
        }
        moveafter("Post Code"; City)        // reorder fields
    }

    actions
    {
        addlast(Processing)
        {
            action(ContosoCalculatePoints)
            {
                ApplicationArea = All;
                Caption = 'Recalculate Points';
                Image = Calculate;
                Promoted = true;
                PromotedCategory = Process;

                trigger OnAction()
                var
                    LoyaltyMgt: Codeunit "Contoso Loyalty Management";
                begin
                    LoyaltyMgt.RecalculatePoints(Rec."No.");
                    CurrPage.Update(false);
                end;
            }
        }
    }

    var
        NameStyleExpr: Text;

    trigger OnAfterGetRecord()
    begin
        NameStyleExpr := GetNameStyle();
    end;
}

Extension placement keywords:

  • addafter(AnchorField) / addbefore(AnchorField) — position relative to existing field
  • addlast(GroupName) / addfirst(GroupName) — at end/start of group
  • modify(FieldName) — change properties of existing field
  • moveafter(Target; FieldToMove) / movebefore(Target; FieldToMove) — reorder

Pattern 5: Field Importance and Promoted Actions

Control visibility and prominence:

// Field importance — controls FastTab collapsed behavior
field(CriticalField; Rec.CriticalField)
{
    ApplicationArea = All;
    Importance = Promoted;         // always visible (even when FastTab collapsed)
}
field(StandardField; Rec.StandardField)
{
    ApplicationArea = All;
    Importance = Standard;         // visible when FastTab expanded (default)
}
field(RareField; Rec.RareField)
{
    ApplicationArea = All;
    Importance = Additional;       // hidden unless "Show more" clicked
}
// Promoted actions — modern actionref syntax (BC 22+)
actions
{
    area(Processing)
    {
        action(Post) { Caption = 'Post'; Image = Post; }
        action(Release) { Caption = 'Release'; Image = ReleaseDoc; }
    }
    area(Promoted)
    {
        group(Category_Process)
        {
            Caption = 'Process';
            actionref(Post_Promoted; Post) { }
            actionref(Release_Promoted; Release) { }
        }
    }
}

Workflow

Step 1: Determine Page Type

NeedPageTypeKey Properties
Single record editingCardUsageCategory = None, has Card groups
Multi-record browsingListCardPageId, Editable = false, FlowField limits
Header + lines editingDocumentpart() with ListPart subpage, UpdatePropagation
Batch data entryWorksheetEditable repeater, no Card drill-down
Dashboard/home pageRoleCenterpart() with cues, activities, headlines
Embed in another pageListPart/CardPartUsed inside part() only

Step 2: Design Layout

  1. Group related fields into FastTabs (group())
  2. Set Importance — Promoted for key fields, Additional for rare ones
  3. Add FactBoxes (area(FactBoxes)) for contextual info
  4. Add Notes and Links system parts
  5. For Document pages, create the ListPart subpage with AutoSplitKey

Step 3: Add Actions and Navigation

  1. Define actions in area(Processing) and area(Navigation)
  2. Promote key actions using area(Promoted) with actionref (BC 22+)
  3. Set appropriate Image identifiers
  4. Add RunObject / RunPageLink for navigation actions

Step 4: Implement Dynamic UI

  1. Visible / Editable expressions — control field/group visibility based on state
  2. StyleExpr — conditional formatting (Favorable, Unfavorable, Attention, Subordinate, Strong)
  3. Enabled — disable actions based on record state
  4. Update expressions in OnAfterGetRecord trigger

Step 5: Build and Test

  1. Build: al_build
  2. Publish: al_publish (incremental)
  3. Verify layout, navigation, and field behavior
  4. Test dynamic visibility and styling with different record states

References

Constraints

  • This skill covers page design, layout, extensions, and dynamic UI patterns
  • Do NOT modify standard BC pages directly — use pageextension objects only
  • Do NOT place more than 3-4 FlowFields on List pages (performance impact)
  • Do NOT skip ApplicationArea on any field or action
  • API page design → skill-api.md | Performance (FlowFields, SetLoadFields) → skill-performance.md | Page testing → skill-testing.md

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.