Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
d39820a
docs(warm-loop): add slice 5 — multi-project solutions in one container
claude Jul 23, 2026
19ecd32
fix(run): reap child process groups so ports free on Ctrl-C
claude Jul 24, 2026
2d352e8
fix(run): re-serve the browser bundle after a structural --watch change
claude Jul 24, 2026
6714965
Merge pull request #12 from ako/claude/mxbuild-diagnostics-spike-emta6h
ako Jul 24, 2026
b971c9d
docs(proposals): add Atlas Design System skill proposal
claude Jul 24, 2026
36c7938
Merge pull request #13 from ako/fix/run-local-warm-loop-reliability
ako Jul 24, 2026
d82ad78
fix(security): reject cross-module document-access grants (CE0148 guard)
claude Jul 24, 2026
29ae36e
feat(mdl): SHOW / DESCRIBE BUILDING BLOCKS (read-only discovery)
claude Jul 24, 2026
60cd320
Merge pull request #14 from ako/docs/atlas-design-proposal
ako Jul 24, 2026
40538a7
Merge pull request #15 from ako/fix/cross-module-grant-ce0148
ako Jul 24, 2026
e532150
feat(catalog): building_blocks catalog table
claude Jul 24, 2026
8634203
Merge pull request #16 from ako/feature/show-describe-building-blocks
ako Jul 24, 2026
8bb9a27
fix(building-blocks): modelsdk ListBuildingBlocks + exec-safe example
claude Jul 24, 2026
7dbb171
Merge pull request #17 from ako/fix/building-blocks-modelsdk-and-example
ako Jul 24, 2026
32b3985
feat(skills): add atlas-design skill for visually appealing Mendix apps
claude Jul 24, 2026
488a2a0
docs(proposals): add USE BUILDING BLOCK (instantiate) follow-up slice
claude Jul 24, 2026
e78e669
feat(mdl): USE BUILDING BLOCK v1 — deep-copy a block onto a page
claude Jul 24, 2026
4da5f43
docs(skills): atlas-design teaches `use building block` (v1) as the p…
claude Jul 24, 2026
932c1db
Merge remote-tracking branch 'origin/main' into feature/atlas-design-…
claude Jul 24, 2026
010b06c
Merge pull request #18 from ako/feature/atlas-design-skill
ako Jul 24, 2026
21efd09
Merge remote-tracking branch 'origin/main' into feature/use-building-…
claude Jul 24, 2026
81f301a
Merge pull request #19 from ako/feature/use-building-block
ako Jul 24, 2026
0ae1026
docs: atlas proposal quick-wins — fix stale chart note, add slider ca…
claude Jul 24, 2026
0e30538
Fix compound (nested) design properties silently dropped on write
claude Jul 24, 2026
7721abc
Merge pull request #20 from ako/docs/atlas-proposal-quickwins
ako Jul 24, 2026
26697aa
Merge pull request #21 from ako/fix/nested-designproperties
ako Jul 24, 2026
9e5d4ab
Add content-slot fragments — wrap arbitrary content in a reusable shell
claude Jul 24, 2026
1be4d07
Add fragment parameter bindings + building-block rebind overrides (ex…
claude Jul 24, 2026
b1590d3
Fix fragment/building-block/slot not expanding inside a container
claude Jul 24, 2026
ca4ddb3
Add focused, CI-checked fragment slot/binding examples; fix pre-exist…
claude Jul 24, 2026
ca7b98c
Merge pull request #22 from ako/feature/fragment-content-slot
ako Jul 24, 2026
948b2da
CI hygiene: gate bug-tests in CI + rename misnamed negative fixture
claude Jul 24, 2026
fb7798d
Merge pull request #24 from ako/chore/ci-check-mdl-hygiene
ako Jul 24, 2026
2a4494a
Merge pull request #25 from ako/feature/fragment-bindings
ako Jul 24, 2026
a4f882f
docs: fix skill inaccuracies found via the sudoku test app (findings …
claude Jul 25, 2026
aad1af2
Fix run --hub: re-register with the hub on heartbeat 404 (findings #16)
claude Jul 25, 2026
b3de82d
docs: seed the autonumber example in generate-domain-model.md too (#6)
claude Jul 25, 2026
4bd9029
Quick code fixes from the sudoku findings (#5, #6, #7, #15, #17, #23)
claude Jul 25, 2026
382e5a2
Make MDL syntax errors point at the fix, not just the location
claude Jul 25, 2026
9165181
docs-site: fix invalid DELETE_CASCADE keyword surfaced by the check-s…
claude Jul 25, 2026
ced3978
Merge pull request #26 from ako/fix/findings-docs
ako Jul 25, 2026
cd14f18
Merge pull request #27 from ako/fix/hub-reregister
ako Jul 25, 2026
93086fb
Merge pull request #28 from ako/fix/findings-quickfixes
ako Jul 25, 2026
87de214
Merge pull request #29 from ako/fix/parse-error-messages
ako Jul 25, 2026
d8f7a26
Entity DDL safety: alter-path attribute checks + create-or-modify dat…
claude Jul 25, 2026
8e9ca6b
Parser errors: only flag real contraction leftovers as unescaped apos…
claude Jul 25, 2026
8314d45
ci: re-trigger (flaky external OpenAPI-fetch timeout, unrelated to ch…
claude Jul 25, 2026
ca6f029
Merge pull request #30 from ako/fix/findings-entity-safety
ako Jul 25, 2026
076bb23
Merge pull request #31 from ako/fix/apostrophe-false-positive
ako Jul 25, 2026
ab2140a
exec: add --continue-on-error so partially-applied scripts re-run
claude Jul 25, 2026
ef1de85
alter entity: idempotent ADD/DROP ATTRIBUTE IF (NOT) EXISTS
claude Jul 25, 2026
23160ea
check: flag unknown expression functions and Decimal-into-integer ass…
claude Jul 25, 2026
6bc927c
Merge pull request #32 from ako/fix/rerunnable-domain-scripts
ako Jul 26, 2026
57442ec
Merge pull request #33 from ako/fix/check-expression-validators
ako Jul 26, 2026
e3ccb55
grammar: accept SQL-like INDEX name ON (cols) in entity definitions
claude Jul 26, 2026
cfda53a
Merge pull request #34 from ako/fix/entity-index-grammar
ako Jul 26, 2026
f8f6f76
check: explain that XPath constraints can't compute values (findings #8)
claude Jul 26, 2026
26aa537
Merge pull request #35 from ako/fix/xpath-arithmetic
ako Jul 26, 2026
41aa3a8
skill: make migrate-design-prototype Atlas-first
claude Jul 26, 2026
a57da40
Merge pull request #37 from ako/docs/migrate-skill-atlas-first
ako Jul 26, 2026
8744867
Typed design properties: registry-driven value types + check validation
claude Jul 26, 2026
8db91bc
Merge pull request #36 from ako/feature/typed-design-properties
ako Jul 26, 2026
1945ae6
run --local: tee the Mendix runtime log to a file for debugging
claude Jul 26, 2026
6d3cde8
Merge pull request #38 from ako/fix/runtime-log
ako Jul 26, 2026
497ab8b
run --local: attach a file log subscriber so application logs are cap…
claude Jul 26, 2026
a2f8d82
Merge pull request #39 from ako/fix/runtime-log-subscriber
ako Jul 26, 2026
b6a7101
Fix CE6084: ToggleButtonGroup design property value must be an option…
claude Jul 26, 2026
96e8740
run --local: start_logging so the attached log subscriber actually de…
claude Jul 26, 2026
144d9c0
Merge pull request #40 from ako/fix/design-property-toggle-group-valu…
ako Jul 26, 2026
5fb58db
Merge pull request #41 from ako/fix/runtime-log-start-logging
ako Jul 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .claude/skills/fix-issue.md

Large diffs are not rendered by default.

784 changes: 784 additions & 0 deletions .claude/skills/mendix/atlas-design.md

Large diffs are not rendered by default.

30 changes: 28 additions & 2 deletions .claude/skills/mendix/create-page.md
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,26 @@ container card1 (OnClick: microflow MyModule.ACT_OpenDetails, class: 'clickable-
This maps to the Mendix Container's **On click** event (`Forms$DivContainer.OnClickAction`).
A container with no `OnClick:`/`Action:` is non-clickable (a no-op action), exactly as in Studio Pro.

**Prefer a clickable container over an `actionbutton` when the trigger needs child
widgets or a context argument.** An `actionbutton` can't contain child widgets and
maps only page-context object parameters — so a tile showing a digit over a label,
or a card that opens a specific object, is best built as a `container` with
`OnClick:`. The container's `OnClick:` takes the same `(Param: …)` argument syntax
as an `actionbutton`'s `action:`:

```sql
-- Rich, parameterised trigger: a card that opens the object it represents
container tileCard (OnClick: microflow MyModule.ACT_Open(Item: $currentObject), class: 'tile') {
dynamictext tileValue (content: '4')
dynamictext tileLabel (content: '4 LEFT', class: 'tile-label')
}
```

Note the Mendix limit this works around: a button (or `OnClick`) can map **object**
parameters from the page context but **cannot pass a literal argument**. To vary a
literal per trigger (e.g. a 1–9 number pad), make one real microflow and a thin
wrapper per value (`ACT_Set1`…`ACT_Set9`), each calling the shared implementation.

### FOOTER Widget

Container for form action buttons:
Expand Down Expand Up @@ -841,8 +861,14 @@ Notes:
restore the v2 files around the normalization, so they are safe; raw
`mx update-widgets` is only safe on a v1 project (or on a throwaway copy used purely
for diagnosis).
- LineChart/BubbleChart/HeatMap (the `line`/`scalecolor` object-lists) are not yet
authorable via MDL — use Studio Pro for those.
- LineChart/BubbleChart/HeatMap/TimeSeries are **also MDL-authorable** (via the
`line`/`scalecolor` object-lists) — see
`mdl-examples/doctype-tests/34-chart-widget-examples.mdl` for working examples.
(They author on the modelsdk engine.)
- **Slider / RangeSlider**: set `showTooltip: false`. The tooltip calls React's
removed `findDOMNode` on Mendix 11, throwing "Could not render widget" on drag — a
runtime crash `mx check` cannot catch (only a running build does). See
`atlas-design.md` for the full runtime-verification rule.

## Complete Examples

Expand Down
83 changes: 83 additions & 0 deletions .claude/skills/mendix/fragments.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,87 @@ use fragment SaveCancelFooter as order_
-- Creates: order_footer1, order_btnSave, order_btnCancel
```

### Content Slots — wrap arbitrary content

A plain fragment substitutes a **fixed** widget group. A **content slot** lets a
fragment **wrap arbitrary caller-supplied content** — a reusable shell (a card,
panel, or section) whose body varies per use. Declare a `slot` where the caller's
widgets should land, then fill it with the `use fragment X { … }` payload form:

```mdl
define fragment Card as {
container cardWrap (class: 'card', designproperties: ['Card style': on]) {
container cardBody (class: 'card-body') {
slot content -- caller's widgets are spliced in here
}
}
};

create page Module.Dashboard (title: 'Dashboard', layout: Atlas_Core.Atlas_Default) {
use fragment Card {
dynamictext cardHeading (content: 'Welcome', rendermode: H2)
dynamictext cardText (content: 'Any widgets can go inside the reusable Card shell')
}
};
```

Rules (v1):
- The slot name is optional and defaults to `content`; a fragment supports one slot.
- Using a slotted fragment with **no** payload (`use fragment Card`) expands the
slot to nothing — a valid empty shell.
- Supplying a payload to a fragment that declares **no** slot is an error.
- The payload is deep-cloned; `as prefix_` still renames the fragment's own
widgets (not the caller's payload).
- The slot resolves at expansion — `describe page` shows the fully-expanded tree
(no slot marker), and there are no BSON/round-trip surprises.

> For varying a leaf **value** (a label or attribute name) rather than wrapping a
> subtree, scalar params (`define fragment F($label, $attr) as …`) are a planned
> v1.1 follow-up; today use a slot plus a one-line value fill.

### Parameter bindings — datasource & action (experimental)

The content slot varies *structure* (which widgets). Typed **parameters** vary
*data* and *behavior*: a fragment can declare a `datasource` and/or an `action`
parameter, reference it with `$name` in a datasource/action position, and receive
its value at the use site. This turns a shell into a real reusable component —
one panel bound to a different entity and a different handler per use.

```mdl
define fragment DataPanel($data: datasource, $onEdit: action) as {
container panelWrap (class: 'card') {
listview lvItems (datasource: $data) {
slot content
actionbutton btnEdit (caption: 'Edit', action: $onEdit, buttonstyle: primary)
}
}
};

create page Module.Orders (title: 'Orders', layout: Atlas_Core.Atlas_Default) {
use fragment DataPanel ($data: database Module.Order, $onEdit: microflow Module.EditOrder) {
dynamictext panelHeading (content: 'Orders', rendermode: H4)
}
};
```

Rules:
- Param kinds are `datasource` (`$var` / `database E` / `$currentObject/Assoc` / `microflow M`) and `action` (a microflow / nanoflow / `save_changes` / `show_page` / …).
- Every declared parameter must be supplied; unknown args and type mismatches are errors.
- Values substitute at expansion — `describe page` shows the concrete datasource/action, no `$param`.

**Building blocks** can't declare params (they're authored in Studio Pro), but a
`use building block` accepts **rebind overrides** that rewrite the block's
outermost datasource and/or its first button after the copy:

```mdl
use building block Atlas_Web_Content.List_Cards
(datasource: database Module.Order, action: microflow Module.OpenOrder) as orders_;
```

Binding-point rule (prototype): datasource → the first widget carrying a
datasource; action → the first button widget. For anything more specific, copy
the block in with `as prefix_` and use `alter page … set … on prefix_widget`.

### SHOW FRAGMENTS

```mdl
Expand Down Expand Up @@ -204,6 +285,8 @@ create page Module.MyPage (...) {
- [ ] Prefix used when the same fragment appears multiple times on one page
- [ ] Fragment widget names don't conflict with other widgets on the page
- [ ] All widgets inside fragments use valid syntax (same as page bodies)
- [ ] A `use fragment X { … }` payload is only supplied when fragment `X` declares a `slot`
- [ ] A slotted fragment has exactly one `slot` (v1 supports a single slot)

## Related Documentation

Expand Down
14 changes: 13 additions & 1 deletion .claude/skills/mendix/generate-domain-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ Mendix supports four built-in auditing properties on persistent entities. Declar
* Order with full audit trail
*/
create persistent entity Sales.Order (
OrderNumber: autonumber,
OrderNumber: autonumber default 1,
TotalAmount: decimal not null,
status: enumeration(Sales.OrderStatus) not null,
owner: autoowner,
Expand Down Expand Up @@ -554,6 +554,18 @@ create persistent entity Module.Address (...);
create persistent entity Module.Order (...);
```

## Modelling for the client: widgets bind members, not expressions

A page widget binds an **attribute or association path** — never an expression. There
is no `substring(attr, i, 1)` or computed binding in a widget. So a value that must be
rendered per-part (each character of a code, each cell of a grid, each of N pencil
marks) has to be stored as **separate attributes**, not packed into one string and
indexed client-side. Model the wide form when the UI needs to address the parts
individually (e.g. `N1`…`N9` booleans rather than a packed `Notes` string); reach for
a computed/derived value only where a microflow or view-entity OQL produces it into a
real attribute. (Same reason the bucket-class idiom exists — see
`migrate-design-prototype.md`.)

## Documentation Best Practices

### Entity Documentation
Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/mendix/java-actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -649,7 +649,7 @@ begin
EmailAddress = $customer/Email
);

if not $isValid then
if not($isValid) then
validation feedback $customer/Email
message 'Please enter a valid email address';
end if;
Expand Down
17 changes: 11 additions & 6 deletions .claude/skills/mendix/mdl-entities.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@ create persistent entity Module.Customer (
-- Enumeration
status: Module.CustomerStatus default Active,

-- Auto-number
CustomerNumber: autonumber
-- Auto-number (REQUIRES a seed via `default N` — without it the build fails
-- CE7247 "Value cannot be empty")
CustomerNumber: autonumber default 1
);
/
```
Expand Down Expand Up @@ -73,7 +74,7 @@ create non-persistent entity Module.CustomerSearchParams (
| DateTime | `Name: datetime` | `CreatedAt: datetime` |
| Date | `Name: date` | `BirthDate: date` |
| Enumeration | `Name: Module.EnumName` | `status: Module.Status` |
| AutoNumber | `Name: autonumber` | `Code: autonumber` |
| AutoNumber | `Name: autonumber default 1` | `Code: autonumber default 1` (seed required) |
| Binary | `Name: binary` | `FileData: binary` |
| Hashed String | `Name: hashedstring` | `password: hashedstring` |

Expand Down Expand Up @@ -253,11 +254,15 @@ create persistent entity Module.Product (
Category: string(50),
Price: decimal
)
index idx_product_code on (Code)
index idx_product_category on (Category);
index idx_product_code (Code)
index idx_product_category (Category);
/
```

> The `on` keyword is optional and reads SQL-like: `index idx_product_code on (Code)`
> is equivalent to `index idx_product_code (Code)`. Multi-column indexes list the
> columns in order: `index idx_pos on (Row, Col)`.

## Complete Domain Model Example

```mdl
Expand Down Expand Up @@ -293,7 +298,7 @@ create persistent entity Shop.Product (

-- Order entity
create persistent entity Shop.Order (
OrderNumber: autonumber,
OrderNumber: autonumber default 1,
OrderDate: datetime not null,
status: Shop.OrderStatus default Draft,
TotalAmount: decimal,
Expand Down
83 changes: 72 additions & 11 deletions .claude/skills/mendix/migrate-design-prototype.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ It covers the two halves of the job:
2. **Apply it in pages** — attach the theme's classes to widgets with MDL
(`Class:` / `DynamicClasses:` on `create page` / `alter page`).

Related skills: `theme-styling.md` (SCSS compilation chain, hot-reload, styling caveats),
Related skills: **`atlas-design.md` (read first — the Atlas-first taste + workflow layer)**,
`theme-styling.md` (SCSS compilation chain, hot-reload, styling caveats),
`create-page.md` (widget syntax), `alter-page.md` (in-place widget edits),
`bulk-widget-updates.md` (apply a class across many widgets).

Expand Down Expand Up @@ -41,14 +42,40 @@ screenshots ──③ reference ──► widgets get Class: / Dyna
screen, open the matching screenshot/handoff for that screen and match it — colours,
spacing, font, component shapes. Do not invent styling the prototype doesn't show.

**Atlas-first (read `atlas-design.md`).** Reproduce the prototype with what Atlas already
gives you *before* hand-writing custom SCSS. In order of preference:

1. **An Atlas building block** — `use building block Atlas_Web_Content.Card` / `Pageheader`
/ `List_Cards` etc. gives you the whole component's markup + styling for free. Discover
with `show building blocks`, inspect with `describe building block`.
2. **Atlas utility classes and typed design properties** — `class:'card'`, `class:'btn btn-primary'`,
`spacing-inner-*`/`spacing-outer-*` for padding/margin, `flex-row`/`flex-column` +
`align-x-*`/`align-y-*` for layout (no `layoutgrid` needed); or the typed equivalents
`designproperties: ['Card style': on]`, `['Background color': 'Brand Primary']`,
`['Spacing': ['margin-bottom': 'L']]`. `mxcli check -p` validates design-property keys
and values (MDL-WIDGET11/12) and lists the allowed values.
3. **Brand-token retune** — map the prototype's tokens onto Atlas brand variables
(`--brand-primary`, `--brand-*`) in `custom-variables.scss` so the whole app inherits
the palette (see "map onto Atlas" in step ①).
4. **Custom `.ss-*` SCSS — for brand identity only.** Reach for a hand-rolled component
class (below) only when Atlas genuinely can't express the shape (bespoke chrome,
fractional-track grids, pixel-exact rows). Hand-rolling `.panel`/`.stat`/`.card` SCSS
that just re-implements what `class:'card'` already does is the single most common
mistake — see `atlas-design.md`.

The rest of this skill (custom SCSS components, `.ss-*` classes, ListView row reshaping) is
**layer 4** — the identity layer you drop to when the first three don't reach the design.

---

## Where the Theme Lives (read this first — it avoids the main friction)

- **Custom styles go inline in `theme/web/main.scss`, AFTER the `@import`s.** mxcli/the
agent generally **cannot create new SCSS partials** (only existing files are writable),
and styles placed after the imports win the cascade over Atlas defaults. Put all your
design tokens and component classes there.
- **Custom styles go in `theme/web/main.scss` AFTER the `@import`s, or in your own
partial.** Styles placed after the imports win the cascade over Atlas defaults. Once
`main.scss` grows, prefer splitting a partial out for readability: create
`theme/web/_<name>.scss` and add `@import "<name>";` after the Atlas imports (the same
cascade-order rule then applies within the partial). New partials **are** creatable —
keep the import order (custom after Atlas) and everything works.
- Use a **project prefix** for every custom class and CSS variable so they never collide
with Atlas or widget CSS. This project uses `ss-` (e.g. `.ss-panel`, `--ss-primary`).
Pick one prefix and use it everywhere.
Expand Down Expand Up @@ -112,11 +139,19 @@ Token checklist to extract from the handoff:

---

## ② Rebuild Components as Classes
## ② Rebuild Components — Atlas block/class first, custom class only for identity

For each repeated element in the prototype (panel, stat tile, chip, card, table row,
progress bar…) write **one reusable class** driven by the tokens from step ①. Keep classes
small and composable so a widget can stack several (`Class: 'ss-panel ss-grid-lv'`).
progress bar…), **first check whether Atlas already provides it** (Atlas-first, above):
is there a building block (`show building blocks`) or an Atlas class / design property
(`card`, `btn-*`, `spacing-*`, `flex-*`+`align-*`, `['Card style': on]`) that gets you
most of the way? If so, use it and add a thin `.ss-*` class only for the brand delta
(colour, radius, font). Re-implementing `card`/`panel`/`btn` from scratch is the mistake
`atlas-design.md` exists to prevent.

When Atlas can't express the shape, write **one reusable class** driven by the tokens from
step ①. Keep classes small and composable so a widget can stack several
(`Class: 'ss-panel ss-grid-lv'`).

```scss
// White surface panel
Expand Down Expand Up @@ -175,8 +210,9 @@ Scheduling and Expense Approval designs.
| Design component | Mendix widget | Notes |
|---|---|---|
| Page / screen canvas | `container` | one per page, e.g. `Class: 'ea-page'` |
| Card / panel / section | `container` | + a panel class |
| Card / panel / section | Atlas `Card` building block, or `container class:'card'` / `['Card style': on]` | drop to a custom panel class only for brand delta |
| KPI / stat tile | `container` | label + value + delta as child `dynamictext` |
| Row/column layout (even columns, gaps) | `container` with `flex-row`/`flex-column` + `align-*` (or `['Flex container': …]`) | no `layoutgrid` needed for simple flex layouts |
| Multi-column / dashboard layout | `layoutgrid` + `row` + `column` | for exact fractional tracks (`2.4fr 1.2fr …`) use a `container` styled `display:grid` instead — see Layout techniques |
| Heading / title | `dynamictext` (RenderMode H1/H2) | |
| Body / label / caption / table cell | `dynamictext` | the workhorse — text is inline, see techniques |
Expand Down Expand Up @@ -416,6 +452,29 @@ container heatCell (

(Note the doubled single-quotes for string literals inside an MDL expression.)

### Computed dimensions — the bucket-class idiom

A widget has **no computed inline style**: `Style:` is a static string and
`DynamicClasses:` returns *class names*, not CSS values. So anything with a
data-driven dimension — a progress bar width, a bar-chart height, a meter fill —
can't be `Style: 'width: {expr}%'`. The idiom is to **quantise the value into a
bucket and generate one class per bucket**:

1. In a microflow, publish an integer bucket attribute (e.g. `0..20`):
`$obj/PctBucket = round($obj/Done / $obj/Total * 20)`.
2. Generate the classes once with an SCSS `@for` loop:

```scss
@for $i from 0 through 20 { .ss-pb-#{$i} { width: $i * 5%; } }
```

3. Select the class from the bucket:
`DynamicClasses: '''ss-pb-'' + toString($currentObject/PctBucket)'`.

Trade-off worth noting: this adds one bucket attribute per animated dimension to the
domain model. Pick a bucket count that matches the visual precision you need (20 → 5%
steps is usually plenty).

### Adding classes to an existing page

Use `alter page` to attach a class without rewriting the page (see `alter-page.md`):
Expand Down Expand Up @@ -467,8 +526,10 @@ for screenshotting the running app. Iterate ②–④ per screen until it matche
`.ss-*-lv` class or rows won't read as the design's grid.
- **For bespoke tables, prefer a styled `listview`** over the `datagrid` pluggable widget —
you control the full row markup, which a pixel-faithful design usually needs.
- **Design-property keys are case-sensitive** — see `theme-styling.md` if you use
`DesignProperties:` instead of raw classes.
- **Prefer typed `designproperties:` / Atlas classes over custom SCSS for anything Atlas
covers** (spacing, alignment, card/background, flex layout) — see `atlas-design.md`. Keys
and values are case-sensitive; `mxcli check -p` validates them (MDL-WIDGET11/12) and lists
the allowed values. `theme-styling.md` has the compilation/reload mechanics.
- **`alter styling` can't find widgets in MDL-builder-created pages** — apply classes via
`Class:`/`DynamicClasses:` in `create page` / `alter page` instead.

Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/mendix/patterns-data-processing.md
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ begin
-- Collect items to remove
loop $item in $Items
begin
if not $item/IsActive then
if not($item/IsActive) then
add $item to $ToRemove;
end if;
end loop;
Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/mendix/resolve-forward-references.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ This applies to the following reference types:
| `snippetcall` | page / snippet | snippet created after the page |
| `show_page` in action | page / snippet | page created after the page that references it |
| `SHOW PAGE` | microflow | page created after the microflow |
| `call microflow` | microflow | already works — microflow resolver checks in-session cache |
| `call microflow` | microflow | callee microflow created after the caller (in the same script, `exec` resolves the call against the project/backend, not later same-script definitions — so order the callee first) |

> **Note:** `SHOW PAGE` inside a microflow body resolves the page reference at
> microflow-creation time, not at invocation time. If the target page doesn't exist yet,
Expand Down
Loading
Loading