Skip to content

[Form Kit] Use spacing tokens and give TabPanel the 8px item gap - #4088

Open
idaiv wants to merge 2 commits into
2026.xfrom
3371-form-kit-spacing-consistency
Open

[Form Kit] Use spacing tokens and give TabPanel the 8px item gap#4088
idaiv wants to merge 2 commits into
2026.xfrom
3371-form-kit-spacing-consistency

Conversation

@idaiv

@idaiv idaiv commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Refs #3371

Scope, and what this does not do

#3371 asks for a consistent 8px vertical gap rule in the form kit. This PR does two things toward that — moves spacing onto tokens, and fixes the one place where the 8px gap was silently not applied at all.

It deliberately does not change the value that governs most forms. studio-default-light.ts sets Form.itemMarginBottom: 12, and 227 files use plain Form.Item against 69 using FormKit — so the majority regime in the app is still 12px, not 8px. Changing it shifts every non-FormKit form by 4px and needs a visual pass, so it is left for a follow-up. The issue should stay open after this merges.

Tokens instead of literals

Was Now Pixels
padding: '8px 16px' ×7 (classification-store-config) `${token.paddingXS}px ${token.padding}px` unchanged
GAP_SIZE = 12 (Region) token.sizeSM unchanged
itemMarginBottom: 8 (language comparison) token.marginXS unchanged
marginTop: 8 / fontSize: '12px' / color: '#666' ×2 (appearance color panel) marginXS / fontSizeSM / colorTextSecondary unchanged
margin-bottom: 10px (reports group divider) token.marginXS 10 → 8px
margin-top: 50px ×2 (thumbnail empty states) token.marginXXL 50 → 48px

Everything except the last two rows is pixel-identical by construction. Region stays at 12px on purpose — it is a region/column gap feeding a calc() width correction, a different axis from field rhythm; tokenising it means the gap and the correction can no longer drift apart.

TabPanel now supplies the item gap

FormKit sets itemMarginBottom: 0 and expects the gap from ItemSpacer, but only Panel supplied one. Fields placed straight into a tab therefore rendered flush — live example in the class editor: the Text layout type's Configuration tab stacks renderingClass, renderingData and the WYSIWYG with no gap (field-definition-text-form-fields.tsx).

Tabpanel now wraps its content in ItemSpacer, mirroring Panel. Fixed at the component rather than per-caller because gap only applies between siblings, so every existing consumer is unaffected:

  • object-tabpanel — one <ObjectComponent> per tab → no change
  • all existing stories — <Panel> per tab → no change
  • gdpr-data-extractor — its own local Tabpanel (takes data/executeSearch) → untouched

Storybook

The TabPanel story now states the contract (8px via ItemSpacer, why it is needed given itemMarginBottom: 0, and that Panel-wrapping does not double up), plus a new FieldsWithoutPanel story rendering the shape that used to break, beside a single-field tab so the no-doubling case is visible too.

Testing

Lint clean on all 13 files; 40 tests pass in components/form, components/tabpanel, components/panel; npm run build-app succeeds.

Not verified visually, and worth a reviewer's eye: ItemSpacer introduces a Flex vertical wrapper inside every tab. Gaps are provably unchanged for single-child tabs, but a flex context can alter how height and overflow propagate, and object-tabpanel feeds ObjectComponent through it in the data-object editor. Tests will not catch that. A look at a data object's edit view would settle it.

Local note: the full-project npm run lint and tsc --noEmit are OOM-killed in my dev container (6 GiB cgroup, exit 137). Per-file lint is clean and the build passes — CI is the gate for those two.

Two things, both toward the consistent 8px field rhythm asked for in #3371.

Spacing now comes from tokens instead of literals:
- `padding: '8px 16px'` (7x across classification-store-config) ->
  `${token.paddingXS}px ${token.padding}px` — same pixels.
- Region's `GAP_SIZE = 12` -> `token.sizeSM`. Same 12px, but it feeds both
  the grid gap and the percentage width correction, so the two can no longer
  drift apart. Left at 12px deliberately: this is a region/column gap, a
  different axis from field rhythm.
- `itemMarginBottom: 8` in the language comparison -> `token.marginXS`.
- Field descriptions in the appearance color panel: `marginTop: 8`,
  `fontSize: '12px'` and `color: '#666'` -> marginXS / fontSizeSM /
  colorTextSecondary.
- Two strays: a reports divider at 10px -> `marginXS` (8px, matching the
  tokens already used around it), and the thumbnail empty states at 50px ->
  `marginXXL` (48px, the nearest step).

TabPanel now spaces its own children:
FormKit sets antd's `itemMarginBottom` to 0 and expects the gap to come from
`ItemSpacer`, but only `Panel` supplied one. Fields placed straight into a tab
rendered flush — visible in the class editor, where the Text layout type's
Configuration tab stacks three inputs with no gap at all. TabPanel now wraps
its content in `ItemSpacer`, mirroring Panel.

This is safe for existing callers because gap only applies between siblings:
`object-tabpanel` passes one component per tab and every story wraps tabs in a
Panel, so both are unchanged. The GDPR extractor has its own local Tabpanel
and is untouched.

Storybook: the TabPanel story documents the contract, and a new
`FieldsWithoutPanel` story covers the shape that used to break, next to a
single-field tab showing nothing doubles up.

Refs #3371

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings September 9, 2026 11:09
@idaiv idaiv added this to the 2026.3.0 milestone Sep 9, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

FormKit-specific spacing must not alter the shared TabPanel behavior for non-FormKit consumers.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Tokenizes form-related spacing and adds 8px spacing between direct TabPanel children.

Changes:

  • Replaces spacing literals with theme tokens.
  • Adds ItemSpacer to TabPanel content.
  • Adds Storybook documentation and examples.
File summaries
File Review
assets/js/src/core/modules/video-thumbnails/video-thumbnails-container.styles.ts Tokenizes empty-state margin.
assets/js/src/core/modules/reports/reports-view/reports-view.styles.tsx Tokenizes divider spacing.
assets/js/src/core/modules/image-thumbnails/image-thumbnails-container.styles.ts Tokenizes empty-state margin.
assets/js/src/core/modules/data-object/editor/toolbar/language-comparison-view/language-comparison-content.tsx Tokenizes form-item spacing.
assets/js/src/core/modules/classification-store-config/components/store-editor/tabs/keys-tab.tsx Tokenizes toolbar padding.
assets/js/src/core/modules/classification-store-config/components/store-editor/tabs/groups/key-group-relations-grid.tsx Tokenizes toolbar padding.
assets/js/src/core/modules/classification-store-config/components/store-editor/tabs/groups-tab.tsx Tokenizes panel padding.
assets/js/src/core/modules/classification-store-config/components/store-editor/tabs/collections/collection-group-relations-grid.tsx Tokenizes toolbar padding.
assets/js/src/core/modules/classification-store-config/components/store-editor/tabs/collections-tab.tsx Tokenizes panel padding.
assets/js/src/core/modules/appearance-branding/components/appearance-form/components/color-panel/color-panel.tsx Tokenizes helper text, but the chosen font and color tokens are not pixel-identical.
assets/js/src/core/components/tabpanel/tabpanel.tsx Adds spacing at the shared API boundary, unintentionally affecting non-FormKit consumers.
assets/js/src/core/components/region/region.styles.ts Tokenizes the region gap.
assets/js/src/core/components/form/layouts/tabpanel/tabpanel.stories.tsx Documents and demonstrates direct-field spacing.
Review details

Suppressed comments (2)

assets/js/src/core/modules/appearance-branding/components/appearance-form/components/color-panel/color-panel.tsx:66

  • This is not pixel-identical under the repository's default theme. The theme seeds fontSize: 12 (dynamic-type-theme-studio-default-light.ts:71), and Ant Design 5.22 derives fontSizeSM as 10px; it also derives colorTextSecondary at 65% black, whereas #666 is 60% black on white. This makes the helper text smaller and darker despite the PR stating this row is unchanged. Use the existing 12px/60%-black semantic tokens to preserve the current rendering.
          <div style={ { marginTop: token.marginXS, fontSize: token.fontSizeSM, color: token.colorTextSecondary } }>

assets/js/src/core/modules/appearance-branding/components/appearance-form/components/color-panel/color-panel.tsx:87

  • As above, fontSizeSM resolves to 10px from this theme's 12px seed and colorTextSecondary resolves to 65% black, so this second description also changes from 12px/#666. Use fontSize and the theme's explicitly configured 60%-black colorTextTertiary token if this conversion is meant to remain visually unchanged.
          <div style={ { marginTop: token.marginXS, fontSize: token.fontSizeSM, color: token.colorTextSecondary } }>
  • Files reviewed: 13/14 changed files
  • Comments generated: 1
  • Review effort level: Balanced

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +85 to +87
<ItemSpacer>
{item.children}
</ItemSpacer>
@sonarqubecloud

sonarqubecloud Bot commented Sep 9, 2026

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants