AgentStack
SKILL verified MIT Self-run

Skill Pages

skill-louage-frw-agentic-coding-skill-pages · by Louage

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

No reviews yet
0 installs
3 views
0.0% view→install

Install

$ agentstack add skill-louage-frw-agentic-coding-skill-pages

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Skill Pages? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

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

| Need | PageType | Key Properties | |---|---|---| | Single record editing | Card | UsageCategory = None, has Card groups | | Multi-record browsing | List | CardPageId, Editable = false, FlowField limits | | Header + lines editing | Document | part() with ListPart subpage, UpdatePropagation | | Batch data entry | Worksheet | Editable repeater, no Card drill-down | | Dashboard/home page | RoleCenter | part() with cues, activities, headlines | | Embed in another page | ListPart/CardPart | Used 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

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.