From b5aa2d822dad60c968658b732b321e4d4491b11f Mon Sep 17 00:00:00 2001 From: Jan Librowski Date: Mon, 7 Sep 2026 14:35:48 +0200 Subject: [PATCH 1/3] fix(ui): move the node shell to DS 2.0 geometry (241px, head padding and gap tokens) Width follows the design master as a canvas dimension in px (it must not scale with the root font size). Padding and vertical gap bind to the node head roles canvas-node-head-h-pad and canvas-node-head-gap from the token export (8px each) instead of the generic space-100 step. --- .changeset/canvas-node-geometry.md | 5 ++ .changeset/connectable-item-width.md | 5 ++ DECISION-LOGS.md | 2 + .../connectable-item-width.decision-log.md | 65 +++++++++++++++++++ .../connectable-item.module.css | 5 +- .../node-section/node-section.module.css | 2 + .../node/node-panel/node-panel.module.css | 6 +- 7 files changed, 85 insertions(+), 5 deletions(-) create mode 100644 .changeset/canvas-node-geometry.md create mode 100644 .changeset/connectable-item-width.md create mode 100644 packages/sdk/src/features/diagram/nodes/components/connectable-item/connectable-item-width.decision-log.md diff --git a/.changeset/canvas-node-geometry.md b/.changeset/canvas-node-geometry.md new file mode 100644 index 000000000..8792e7a6e --- /dev/null +++ b/.changeset/canvas-node-geometry.md @@ -0,0 +1,5 @@ +--- +'@workflowbuilder/ui': patch +--- + +Canvas nodes follow the Design System 2.0 geometry: the default node shell width is the designed 241px (`--wb-public-node-width: 241px`, a canvas dimension that no longer scales with the root font size), and the node shell padding and vertical gap bind to the node head roles `--wb-ds-canvas-node-head-h-pad` and `--wb-ds-canvas-node-head-gap` (8px each) instead of the generic spacing step. Overrides of the public node variables keep working unchanged. diff --git a/.changeset/connectable-item-width.md b/.changeset/connectable-item-width.md new file mode 100644 index 000000000..4911e7ea7 --- /dev/null +++ b/.changeset/connectable-item-width.md @@ -0,0 +1,5 @@ +--- +'@workflowbuilder/sdk': patch +--- + +Connectable node items (Decision branches, AI tools) size their width from the real shell and section insets instead of a fixed multiple of the node padding, so labels keep the full available width after the node shell spacing change. Containers that wrap connectable items can declare their horizontal inset with `--wb-sdk-connectable-item-inset`. diff --git a/DECISION-LOGS.md b/DECISION-LOGS.md index cc8cddf5c..7944eebb7 100644 --- a/DECISION-LOGS.md +++ b/DECISION-LOGS.md @@ -22,4 +22,6 @@ - _15.05.2026_: [AuthPort seam for backend authn/authz](./apps/backend/auth-port.decision-log.md) - _03.06.2026_: [TenantContextPort — multi-tenant identity seam for the reference backend](./apps/backend/tenant-context-port.decision-log.md) - _07.08.2026_: [Keep the postcss box-sizing plugin over lint-based or selector-based alternatives](./packages/ui/postcss-box-sizing.decision-log.md) +- _24.08.2026_: [`incomplete` as a third terminal state, distinct from `failed` and from a stall](./packages/execution-core/terminal-states.decision-log.md) - _31.08.2026_: [Ship common font faces inline and the rest as assets](./packages/ui/font-assets.decision-log.md) +- _07.09.2026_: [Derive the ConnectableItem width from the real container insets](./packages/sdk/src/features/diagram/nodes/components/connectable-item/connectable-item-width.decision-log.md) diff --git a/packages/sdk/src/features/diagram/nodes/components/connectable-item/connectable-item-width.decision-log.md b/packages/sdk/src/features/diagram/nodes/components/connectable-item/connectable-item-width.decision-log.md new file mode 100644 index 000000000..a48d7ad8e --- /dev/null +++ b/packages/sdk/src/features/diagram/nodes/components/connectable-item/connectable-item-width.decision-log.md @@ -0,0 +1,65 @@ +### Title: Derive the ConnectableItem width from the real container insets + +### Proposed by: Jan Librowski + +### Date: 07.09.2026 + +## Context + +`ConnectableItem` (a node body row that carries its own port: Decision branches, AI tools) caps its +width with an absolute `max-width` computed from the public node width. The cap is needed because the +Decision template sets `min-width: max-content` on its body, so a long branch label would otherwise +widen the whole node instead of truncating. + +The previous rule was: + +```css +max-width: calc( + var(--wb-public-node-width) - (6 * var(--wb-public-node-padding)) + 2 * + var(--wb-sdk-connectable-item-horizontal-padding) + 2 * var(--wb-sdk-connectable-item-border-width) +); +``` + +Two problems surfaced when the node shell moved to the DS 2.0 geometry (width 241px, padding 16px): + +- The `6 *` factor is not documented anywhere. With the old 8px shell padding it happened to land + near the real horizontal insets (2 x (8 + 1) shell + 2 x (10 + 1) section = 40px vs 48px); with + 16px it over-subtracts (96px against 58px of real insets), truncating labels about 65px earlier + than the available space requires. +- The `+ 2 * padding + 2 * border` terms assume content-box sizing. The SDK applies a global + `box-sizing: border-box` reset (`packages/sdk/src/index.css`), so `max-width` already refers to + the border box and the terms inflate the cap. + +The design system does not specify a width for these items (register: node geometry gaps). Any cap +is therefore a provisional implementation decision, to be revisited when the design provides one. + +## Decision + +The cap is derived from named insets between the node's outer edge and the item: + +```css +max-width: calc( + var(--wb-public-node-width) - 2 * (var(--wb-public-node-padding) + var(--wb-public-node-border-size)) - 2 * + var(--wb-sdk-connectable-item-inset) +); +``` + +`--wb-sdk-connectable-item-inset` is the horizontal inset (one side) added by the container that +wraps the items. It defaults to `0rem` and each wrapping container declares its own value: + +- `NodeSection` sets it to its padding plus border width, so Decision branches inside a section get + `241 - 2 x (16 + 1) - 2 x (10 + 1) = 185px` (previously `258 - 2 x (8 + 1) - 2 x (10 + 1) = 218px`). +- The AI template places its tool items directly in the content column without horizontal padding, + so the default `0rem` applies there. + +A new wrapper with horizontal padding must set the variable on its container; otherwise its items +may exceed the visible width by that padding. + +## Consequences + +- Item width follows the shell geometry exactly; the only shrink after the DS 2.0 change is the + 33px lost to the wider shell padding, not the 65px the magic factor would have produced. +- The variable makes the nesting explicit and reviewable per container instead of encoding it in a + single global multiplier. +- Provisional until the design specifies the item width; recorded as a decision made without a + design in the DS 2.0 divergence register. diff --git a/packages/sdk/src/features/diagram/nodes/components/connectable-item/connectable-item.module.css b/packages/sdk/src/features/diagram/nodes/components/connectable-item/connectable-item.module.css index ccf0e43b1..288ceb344 100644 --- a/packages/sdk/src/features/diagram/nodes/components/connectable-item/connectable-item.module.css +++ b/packages/sdk/src/features/diagram/nodes/components/connectable-item/connectable-item.module.css @@ -5,6 +5,7 @@ --wb-sdk-connectable-item-border-width: 0.0625rem; --wb-sdk-connectable-item-border-color: var(--wb-ds-components-text-field-stroke-default); --wb-sdk-connectable-item-border-radius: 0.375rem; + --wb-sdk-connectable-item-inset: 0rem; } .connectable-item { @@ -13,8 +14,8 @@ position: relative; display: flex; max-width: calc( - var(--wb-public-node-width) - (6 * var(--wb-public-node-padding)) + 2 * - var(--wb-sdk-connectable-item-horizontal-padding) + 2 * var(--wb-sdk-connectable-item-border-width) + var(--wb-public-node-width) - 2 * (var(--wb-public-node-padding) + var(--wb-public-node-border-size)) - 2 * + var(--wb-sdk-connectable-item-inset) ); padding: var(--wb-sdk-connectable-item-vertical-padding) var(--wb-sdk-connectable-item-horizontal-padding); background-color: var(--wb-sdk-connectable-item-background); diff --git a/packages/sdk/src/features/diagram/nodes/components/node-section/node-section.module.css b/packages/sdk/src/features/diagram/nodes/components/node-section/node-section.module.css index 2bb941f45..b293a982f 100644 --- a/packages/sdk/src/features/diagram/nodes/components/node-section/node-section.module.css +++ b/packages/sdk/src/features/diagram/nodes/components/node-section/node-section.module.css @@ -7,6 +7,8 @@ } .container { + --wb-sdk-connectable-item-inset: calc(var(--wb-sdk-node-section-padding) + var(--wb-sdk-node-section-border-width)); + display: flex; flex-direction: column; gap: var(--wb-sdk-node-section-gap); diff --git a/packages/ui/src/components/node/node-panel/node-panel.module.css b/packages/ui/src/components/node/node-panel/node-panel.module.css index e08fc7909..4a8e962f0 100644 --- a/packages/ui/src/components/node/node-panel/node-panel.module.css +++ b/packages/ui/src/components/node/node-panel/node-panel.module.css @@ -1,10 +1,10 @@ :root { - --wb-public-node-width: 16.125rem; + --wb-public-node-width: 241px; --wb-public-node-height: 100%; --wb-public-node-border-size: 0.0625rem; - --wb-public-node-padding: var(--wb-ds-space-100); - --wb-public-node-gap: var(--wb-ds-space-100); + --wb-public-node-padding: var(--wb-ds-canvas-node-head-h-pad); + --wb-public-node-gap: var(--wb-ds-canvas-node-head-gap); --wb-public-node-border-radius: var(--wb-ds-canvas-node-head-radius); --wb-public-node-border-color: var(--wb-ds-canvas-node-stroke-default); From 6f87d57c56098a18b8382a8277c6617e2631acd9 Mon Sep 17 00:00:00 2001 From: Jan Librowski Date: Mon, 7 Sep 2026 14:35:55 +0200 Subject: [PATCH 2/3] fix(sdk): derive the ConnectableItem width from the real container insets Replaces the undocumented 6 * padding factor with named insets (shell padding + border, plus the wrapping container's own inset declared via --wb-sdk-connectable-item-inset). The old content-box correction terms are dropped because the SDK applies a global border-box reset. --- .changeset/connectable-item-width.md | 2 +- .../node-wrapper-info.module.css | 2 ++ .../connectable-item-width.decision-log.md | 31 ++++++++++--------- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/.changeset/connectable-item-width.md b/.changeset/connectable-item-width.md index 4911e7ea7..451976080 100644 --- a/.changeset/connectable-item-width.md +++ b/.changeset/connectable-item-width.md @@ -2,4 +2,4 @@ '@workflowbuilder/sdk': patch --- -Connectable node items (Decision branches, AI tools) size their width from the real shell and section insets instead of a fixed multiple of the node padding, so labels keep the full available width after the node shell spacing change. Containers that wrap connectable items can declare their horizontal inset with `--wb-sdk-connectable-item-inset`. +Decision branch rows and AI tool rows derive their width cap from the node shell and their container insets, so long labels keep the full available width after the node shell spacing change instead of truncating early. diff --git a/packages/sdk/src/features/diagram/nodes/ai-agent-node-template/components/node-info-wrapper/node-wrapper-info.module.css b/packages/sdk/src/features/diagram/nodes/ai-agent-node-template/components/node-info-wrapper/node-wrapper-info.module.css index 1f00a8753..79cf17881 100644 --- a/packages/sdk/src/features/diagram/nodes/ai-agent-node-template/components/node-info-wrapper/node-wrapper-info.module.css +++ b/packages/sdk/src/features/diagram/nodes/ai-agent-node-template/components/node-info-wrapper/node-wrapper-info.module.css @@ -1,4 +1,6 @@ .container { + --wb-sdk-connectable-item-inset: calc(0.625rem + 1px); + display: flex; flex-direction: column; gap: 0.75rem; diff --git a/packages/sdk/src/features/diagram/nodes/components/connectable-item/connectable-item-width.decision-log.md b/packages/sdk/src/features/diagram/nodes/components/connectable-item/connectable-item-width.decision-log.md index a48d7ad8e..c361456b1 100644 --- a/packages/sdk/src/features/diagram/nodes/components/connectable-item/connectable-item-width.decision-log.md +++ b/packages/sdk/src/features/diagram/nodes/components/connectable-item/connectable-item-width.decision-log.md @@ -20,15 +20,15 @@ max-width: calc( ); ``` -Two problems surfaced when the node shell moved to the DS 2.0 geometry (width 241px, padding 16px): +Two problems surfaced while moving the node shell to the DS 2.0 geometry (width 241px): -- The `6 *` factor is not documented anywhere. With the old 8px shell padding it happened to land - near the real horizontal insets (2 x (8 + 1) shell + 2 x (10 + 1) section = 40px vs 48px); with - 16px it over-subtracts (96px against 58px of real insets), truncating labels about 65px earlier - than the available space requires. +- The `6 *` factor is not documented anywhere. It only approximates the real horizontal insets + between the node edge and the item: shell padding plus border on both sides (2 x 9px) and the + section padding plus border on both sides (2 x 11px), 40px in total against the 48px it subtracts. - The `+ 2 * padding + 2 * border` terms assume content-box sizing. The SDK applies a global `box-sizing: border-box` reset (`packages/sdk/src/index.css`), so `max-width` already refers to - the border box and the terms inflate the cap. + the border box and the terms inflate the cap: at 241px the old rule allows 219px while only 201px + are available inside a section, so a long label could overflow its section by 18px. The design system does not specify a width for these items (register: node geometry gaps). Any cap is therefore a provisional implementation decision, to be revisited when the design provides one. @@ -48,18 +48,21 @@ max-width: calc( wraps the items. It defaults to `0rem` and each wrapping container declares its own value: - `NodeSection` sets it to its padding plus border width, so Decision branches inside a section get - `241 - 2 x (16 + 1) - 2 x (10 + 1) = 185px` (previously `258 - 2 x (8 + 1) - 2 x (10 + 1) = 218px`). -- The AI template places its tool items directly in the content column without horizontal padding, - so the default `0rem` applies there. + `241 - 2 x (8 + 1) - 2 x (10 + 1) = 201px` (with the previous 258px shell: 218px). +- The AI template wraps its tool rows in `NodeInfoWrapper` (padding 0.625rem plus a 1px border + per side), which therefore declares the same inset, so tool rows get + `241 - 2 x (8 + 1) - 2 x (10 + 1) = 201px` as well. The default `0rem` applies only to a + container that adds no horizontal padding. -A new wrapper with horizontal padding must set the variable on its container; otherwise its items -may exceed the visible width by that padding. +The variable is not cumulative: a wrapper declares the inset it adds itself, and a container that +adds horizontal padding without declaring it lets its rows exceed the visible width by that padding. ## Consequences -- Item width follows the shell geometry exactly; the only shrink after the DS 2.0 change is the - 33px lost to the wider shell padding, not the 65px the magic factor would have produced. +- Item width follows the shell geometry exactly and can no longer exceed the space its container + actually offers. - The variable makes the nesting explicit and reviewable per container instead of encoding it in a single global multiplier. - Provisional until the design specifies the item width; recorded as a decision made without a - design in the DS 2.0 divergence register. + design in the DS 2.0 divergence register. The design's node body matrix (row padding 8px, + radius 4px) is a separate follow-up and does not change this derivation. From 6cfe59db80beacb03d4307369ef5233608bec49d Mon Sep 17 00:00:00 2001 From: Jan Librowski Date: Mon, 14 Sep 2026 21:52:16 +0200 Subject: [PATCH 3/3] docs(sdk): describe the ConnectableItem cap change and its follow-up in shipped terms Review follow-up. The changeset states the actual effect (the cap narrows to the space the section offers, so labels truncate instead of overflowing). The decision log drops pointers to a register outside the repo and names the pending design work with a grep-able slug. --- .changeset/connectable-item-width.md | 2 +- .../connectable-item-width.decision-log.md | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.changeset/connectable-item-width.md b/.changeset/connectable-item-width.md index 451976080..e393c0299 100644 --- a/.changeset/connectable-item-width.md +++ b/.changeset/connectable-item-width.md @@ -2,4 +2,4 @@ '@workflowbuilder/sdk': patch --- -Decision branch rows and AI tool rows derive their width cap from the node shell and their container insets, so long labels keep the full available width after the node shell spacing change instead of truncating early. +Decision branch rows and AI tool rows cap their width to the space their section actually offers, so long labels truncate at the section edge instead of overflowing it. diff --git a/packages/sdk/src/features/diagram/nodes/components/connectable-item/connectable-item-width.decision-log.md b/packages/sdk/src/features/diagram/nodes/components/connectable-item/connectable-item-width.decision-log.md index c361456b1..cfc7b3331 100644 --- a/packages/sdk/src/features/diagram/nodes/components/connectable-item/connectable-item-width.decision-log.md +++ b/packages/sdk/src/features/diagram/nodes/components/connectable-item/connectable-item-width.decision-log.md @@ -30,8 +30,9 @@ Two problems surfaced while moving the node shell to the DS 2.0 geometry (width the border box and the terms inflate the cap: at 241px the old rule allows 219px while only 201px are available inside a section, so a long label could overflow its section by 18px. -The design system does not specify a width for these items (register: node geometry gaps). Any cap -is therefore a provisional implementation decision, to be revisited when the design provides one. +The design system does not specify a width for these rows. Any cap is therefore a provisional +implementation decision, to be revisited when the design publishes one +(follow-up: connectable-item-design-width). ## Decision @@ -63,6 +64,6 @@ adds horizontal padding without declaring it lets its rows exceed the visible wi actually offers. - The variable makes the nesting explicit and reviewable per container instead of encoding it in a single global multiplier. -- Provisional until the design specifies the item width; recorded as a decision made without a - design in the DS 2.0 divergence register. The design's node body matrix (row padding 8px, - radius 4px) is a separate follow-up and does not change this derivation. +- Provisional until the design specifies the row width (follow-up: connectable-item-design-width). + The design's node body matrix (row padding 8px, radius 4px) is a separate change and does not + alter this derivation.