Skill Pages logo

Skill Pages

Community
javiarmesto
skill-pages

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

Overview

Publisherjaviarmesto
RepositoryALDC-AL-Development-Collection
Skill nameskill-pages
Stars
115
Forks
37
Bundled files
Instructions only
LicenseMIT
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • Self-contained

    Everything the model needs lives in the instructions — no extra files to sync.

  • Open source

    Published by javiarmesto on GitHub. Read the source before you install it.

Installation

Install the Skill Pages AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
    https://github.com/javiarmesto/ALDC-AL-Development-Collection/tree/main/.claude/skills/skill-pages
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/javiarmesto/ALDC-AL-Development-Collection.git /tmp/ALDC-AL-Development-Collection
mkdir -p .claude/skills
cp -r /tmp/ALDC-AL-Development-Collection/.claude/skills/skill-pages .claude/skills/skill-pages
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Skill Pages in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Skill Pages on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Skill Pages is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

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:

al
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:

al
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)

al
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
            }
        }
    }
}
al
// 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:

al
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:

al
// 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
}
al
// 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

Frequently asked questions

What does the Skill Pages AI skill do?

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

Why use Skill Pages on TypingMind?

Because you install it once and use it with any model. Skill Pages is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Skill Pages in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/javiarmesto/ALDC-AL-Development-Collection/tree/main/.claude/skills/skill-pages. TypingMind reads its SKILL.md and installs it as a skill you can enable per chat.

Which AI models can use Skill Pages?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Skill Pages?

As many as you like. As long as a model supports skills, you can use Skill Pages with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Skill Pages AI skill free?

Yes. It is published on GitHub by javiarmesto under the MIT license. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇