From 91adab7406d6b67542cf0cb363117c4645ff94af Mon Sep 17 00:00:00 2001 From: Zach Shallbetter Date: Mon, 10 Aug 2026 16:05:38 -0700 Subject: [PATCH] feat: support axis-specific padding component sub-tokens Implemented support for axis-specific padding properties (paddingX and paddingY) inside component token definitions (resolves Issue #160). * Background & Motivation: - By default, the linter and spec support a uniform 'padding' sub-token for components. However, asymmetric layouts (e.g. button rows or inputs with larger horizontal than vertical insets) are standard in production design systems. Adding 'paddingX' and 'paddingY' solves this gap natively. - This matches common framework models (Tailwind's px/py, SwiftUI, Flutter) and avoids nested layout complexity (such as the schemas discussed in Issue #17). - This aligns with the philosophy of PR #89 (avoiding CSS property bloat like paddingLeft/paddingTop) by introducing structural layout minimums rather than exhaustive box-model properties. - Sourced independently from PR #164 to prevent merge conflicts. * Changes: - Add 'paddingX' and 'paddingY' to component_sub_tokens list in spec-config.yaml. - Update the valid component properties list in README.md. - Regenerate docs/spec.md using the compiler script. * Test Coverage: - Add validation tests in broken-ref.test.ts to verify that components using paddingX and paddingY do not trigger unrecognized sub-token warnings. --- README.md | 2 +- docs/spec.md | 2 ++ .../cli/src/linter/linter/rules/broken-ref.test.ts | 10 ++++++++++ packages/cli/src/linter/spec-config.yaml | 4 ++++ 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ed855c52..2b153832 100644 --- a/README.md +++ b/README.md @@ -166,7 +166,7 @@ components: backgroundColor: "{colors.tertiary-container}" ``` -Valid component properties: `backgroundColor`, `textColor`, `typography`, `rounded`, `padding`, `size`, `height`, `width`. +Valid component properties: `backgroundColor`, `textColor`, `typography`, `rounded`, `padding`, `paddingX`, `paddingY`, `size`, `height`, `width`. Variants (hover, active, pressed) are expressed as separate component entries with a related key name. diff --git a/docs/spec.md b/docs/spec.md index 5995e548..cabb379f 100644 --- a/docs/spec.md +++ b/docs/spec.md @@ -336,6 +336,8 @@ Each component has a set of properties that are themselves design tokens: - typography: \ - rounded: \ - padding: \ +- paddingX: \ +- paddingY: \ - size: \ - height: \ - width: \ diff --git a/packages/cli/src/linter/linter/rules/broken-ref.test.ts b/packages/cli/src/linter/linter/rules/broken-ref.test.ts index 13ac68aa..44432944 100644 --- a/packages/cli/src/linter/linter/rules/broken-ref.test.ts +++ b/packages/cli/src/linter/linter/rules/broken-ref.test.ts @@ -46,6 +46,16 @@ describe('brokenRef', () => { expect(subTokenDiag!.severity).toBe('warning'); }); + it('does not emit warning for paddingX and paddingY sub-tokens', () => { + const state = buildState({ + colors: { primary: '#ff0000' }, + components: { button: { paddingX: '12px', paddingY: '16px' } }, + }); + const findings = brokenRef(state); + const subTokenDiag = findings.filter(d => d.message.includes('not a recognized')); + expect(subTokenDiag.length).toBe(0); + }); + it('has a valid rule descriptor', () => { expect(brokenRefRule.name).toBe('broken-ref'); expect(brokenRefRule.severity).toBe('error'); diff --git a/packages/cli/src/linter/spec-config.yaml b/packages/cli/src/linter/spec-config.yaml index 6b1e90fd..8aaa74a1 100644 --- a/packages/cli/src/linter/spec-config.yaml +++ b/packages/cli/src/linter/spec-config.yaml @@ -93,6 +93,10 @@ component_sub_tokens: type: Dimension - name: padding type: Dimension + - name: paddingX + type: Dimension + - name: paddingY + type: Dimension - name: size type: Dimension - name: height