@3fn/core 13.0.0 → 14.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.kiro/agents/ada-prompt.md +80 -132
- package/.kiro/agents/ada-prompt.md.attribution.json +45 -0
- package/.kiro/agents/ada.json +44 -59
- package/.kiro/agents/ada.json.attribution.json +13 -0
- package/.kiro/agents/data-prompt.md +83 -74
- package/.kiro/agents/data-prompt.md.attribution.json +53 -0
- package/.kiro/agents/data.json +31 -38
- package/.kiro/agents/data.json.attribution.json +13 -0
- package/.kiro/agents/kenya-prompt.md +83 -72
- package/.kiro/agents/kenya-prompt.md.attribution.json +53 -0
- package/.kiro/agents/kenya.json +27 -35
- package/.kiro/agents/kenya.json.attribution.json +13 -0
- package/.kiro/agents/leonardo-prompt.md +176 -234
- package/.kiro/agents/leonardo-prompt.md.attribution.json +45 -0
- package/.kiro/agents/leonardo.json +28 -36
- package/.kiro/agents/leonardo.json.attribution.json +13 -0
- package/.kiro/agents/lina-prompt.md +110 -151
- package/.kiro/agents/lina-prompt.md.attribution.json +53 -0
- package/.kiro/agents/lina.json +46 -59
- package/.kiro/agents/lina.json.attribution.json +13 -0
- package/.kiro/agents/sparky-prompt.md +89 -72
- package/.kiro/agents/sparky-prompt.md.attribution.json +53 -0
- package/.kiro/agents/sparky.json +37 -37
- package/.kiro/agents/sparky.json.attribution.json +13 -0
- package/.kiro/agents/stacy-prompt.md +74 -48
- package/.kiro/agents/stacy-prompt.md.attribution.json +45 -0
- package/.kiro/agents/stacy.json +28 -30
- package/.kiro/agents/stacy.json.attribution.json +13 -0
- package/.kiro/agents/thurgood-prompt.md +94 -138
- package/.kiro/agents/thurgood-prompt.md.attribution.json +45 -0
- package/.kiro/agents/thurgood.json +31 -35
- package/.kiro/agents/thurgood.json.attribution.json +13 -0
- package/.kiro/steering/AI-Collaboration-Principles.md +3 -3
- package/.kiro/steering/Civitas-System-Overview.md +7 -16
- package/.kiro/steering/DesignerPunk-Systems-Overview.md +6 -6
- package/.kiro/steering/Spec-Feedback-Protocol.md +2 -11
- package/.kiro/steering/Task-Completion-Protocol.md +98 -17
- package/.kiro/steering/core-goals.md +3 -3
- package/.kiro/steering/personal-note.md +1 -1
- package/.kiro/steering/start-up-tasks.md +17 -6
- package/application-mcp-server/src/index.ts +26 -0
- package/dist/ComponentTokens.android.kt +12 -12
- package/dist/ComponentTokens.ios.swift +12 -12
- package/dist/ComponentTokens.web.css +3 -3
- package/dist/DesignTokens.android.kt +1 -1
- package/dist/DesignTokens.dtcg.json +8 -5
- package/dist/DesignTokens.figma.json +2 -2
- package/dist/DesignTokens.ios.swift +1 -1
- package/dist/DesignTokens.web.css +1 -1
- package/dist/android/DesignTokens.android.kt +1 -1
- package/dist/blend/OklchBlendCalculator.js +1 -0
- package/dist/blend/ThemeAwareBlendUtilities.web.d.ts +13 -2
- package/dist/blend/ThemeAwareBlendUtilities.web.js +6 -1
- package/dist/browser/designerpunk.esm.js +36 -90
- package/dist/browser/designerpunk.esm.min.js +33 -36
- package/dist/browser/designerpunk.umd.js +36 -90
- package/dist/browser/designerpunk.umd.min.js +47 -50
- package/dist/browser/tokens.css +3 -3
- package/dist/build/tokens/defineComponentTokens.d.ts +10 -0
- package/dist/build/tokens/defineComponentTokens.js +26 -0
- package/dist/components/core/Avatar-Base/avatar.tokens.d.ts +21 -26
- package/dist/components/core/Avatar-Base/avatar.tokens.js +31 -34
- package/dist/components/core/Avatar-Base/index.d.ts +1 -1
- package/dist/components/core/Avatar-Base/index.js +2 -2
- package/dist/components/core/Avatar-Base/platforms/web/Avatar.web.js +24 -5
- package/dist/components/core/Button-CTA/examples/BasicUsage.d.ts +16 -28
- package/dist/components/core/Button-CTA/examples/BasicUsage.js +18 -43
- package/dist/components/core/Button-CTA/platforms/web/ButtonCTA.web.d.ts +3 -15
- package/dist/components/core/Button-CTA/platforms/web/ButtonCTA.web.js +9 -58
- package/dist/components/core/Button-CTA/types.d.ts +0 -24
- package/dist/components/core/Button-CTA/types.js +6 -0
- package/dist/components/core/Button-Icon/buttonIcon.tokens.d.ts +28 -14
- package/dist/components/core/Button-Icon/buttonIcon.tokens.js +35 -20
- package/dist/components/core/Input-Text-Base/types.d.ts +13 -1
- package/dist/components/core/Input-Text-Password/platforms/web/InputTextPassword.web.js +11 -2
- package/dist/generators/DTCGFormatGenerator.js +8 -0
- package/dist/generators/TokenFileGenerator.js +7 -2
- package/dist/integration/BuildErrorHandler.js +2 -2
- package/dist/ios/DesignTokens.ios.swift +1 -1
- package/dist/mcp/application-mcp.js +24 -0
- package/dist/mcp/docs-mcp.js +130 -15
- package/dist/mcp/product-mcp.js +25 -0
- package/dist/tokens/OpacityTokens.js +1 -1
- package/dist/tokens/component/progress.d.ts +65 -5
- package/dist/tokens/component/progress.js +79 -18
- package/dist/tokens/semantic/BlendTokens.d.ts +10 -3
- package/dist/tokens/semantic/BlendTokens.js +17 -5
- package/dist/tokens/semantic/OpacityTokens.d.ts +4 -4
- package/dist/tokens/semantic/OpacityTokens.js +4 -4
- package/dist/types/ComponentTypes.d.ts +1 -1
- package/dist/types/generated/TokenTypes.d.ts +1 -1
- package/dist/types/generated/TokenTypes.js +1 -1
- package/dist/validators/StemmaTokenUsageValidator.js +3 -2
- package/dist/web/DesignTokens.web.css +1 -1
- package/governance/BUILD-SYSTEM-SETUP.md +1 -2
- package/governance/Component-Development-Guide.md +23 -13
- package/governance/Component-Development-Standards.md +21 -20
- package/governance/Component-Family-Avatar.md +6 -7
- package/governance/Component-Family-Badge.md +19 -20
- package/governance/Component-Family-Button.md +30 -43
- package/governance/Component-Family-Chip.md +14 -15
- package/governance/Component-Family-Container.md +12 -13
- package/governance/Component-Family-Data-Display.md +1 -2
- package/governance/Component-Family-Divider.md +1 -2
- package/governance/Component-Family-Form-Inputs.md +65 -65
- package/governance/Component-Family-Icon.md +9 -10
- package/governance/Component-Family-Loading.md +1 -2
- package/governance/Component-Family-Modal.md +1 -2
- package/governance/Component-Family-Navigation.md +1 -2
- package/governance/Component-Family-Progress.md +0 -1
- package/governance/Component-Inheritance-Structures.md +219 -99
- package/governance/Component-MCP-Document-Template.md +6 -5
- package/governance/Component-Primitive-vs-Semantic-Philosophy.md +1 -1
- package/governance/Component-Quick-Reference.md +33 -33
- package/governance/Component-Readiness-Status.md +59 -44
- package/governance/Component-Templates.md +57 -61
- package/governance/Contract-System-Reference.md +7 -7
- package/governance/MCP-Integration-Guide.md +1 -1
- package/governance/Process-Cross-Reference-Standards.md +31 -13
- package/governance/Process-Development-Workflow.md +49 -59
- package/governance/Process-File-Organization.md +25 -28
- package/governance/Process-Hook-Operations.md +22 -11
- package/governance/Process-Orchestration-Model-Selection.md +92 -0
- package/governance/Process-Spec-Planning.md +98 -52
- package/governance/Process-Task-Type-Definitions.md +80 -4
- package/governance/Product-Handoff-Protocol.md +2 -0
- package/governance/Rosetta-System-Architecture.md +13 -11
- package/governance/Test-Behavioral-Contract-Validation.md +38 -31
- package/governance/Test-Failure-Audit-Methodology.md +1 -1
- package/governance/Token-Family-Accessibility.md +1 -2
- package/governance/Token-Family-Blend.md +18 -16
- package/governance/Token-Family-Blur.md +0 -1
- package/governance/Token-Family-Border.md +1 -2
- package/governance/Token-Family-Color.md +0 -1
- package/governance/Token-Family-Glow.md +1 -2
- package/governance/Token-Family-Layering.md +0 -1
- package/governance/Token-Family-Motion.md +1 -2
- package/governance/Token-Family-Opacity.md +0 -1
- package/governance/Token-Family-Radius.md +1 -2
- package/governance/Token-Family-Responsive.md +1 -2
- package/governance/Token-Family-Shadow.md +1 -2
- package/governance/Token-Family-Sizing.md +0 -1
- package/governance/Token-Family-Spacing.md +1 -2
- package/governance/Token-Family-Typography.md +1 -2
- package/governance/Token-Governance.md +8 -8
- package/governance/Token-Quick-Reference.md +47 -34
- package/governance/Token-Resolution-Patterns.md +1 -1
- package/governance/Token-Semantic-Structure.md +1 -1
- package/governance/Web-Authoring-Standards.md +5 -5
- package/governance/browser-distribution-guide.md +1 -4
- package/governance/classification-map.md +474 -0
- package/governance/completion-documentation-guide.md +23 -37
- package/governance/component-meta-authoring-guide.md +1 -1
- package/governance/cross-platform-vs-platform-specific-decision-framework.md +1 -1
- package/governance/platform-implementation-guidelines.md +2 -3
- package/governance/release-management-system.md +28 -63
- package/governance/rosetta-system-principles.md +8 -6
- package/governance/stemma-system-principles.md +18 -17
- package/mcp-server/src/index.ts +24 -6
- package/mcp-server/src/indexer/DocumentIndexer.ts +119 -9
- package/mcp-server/src/indexer/__tests__/bare-id-crossrefs.test.ts +250 -0
- package/mcp-server/src/indexer/cross-ref-parser.ts +29 -1
- package/mcp-server/src/indexer/index-health.ts +27 -2
- package/mcp-server/src/query/__tests__/find-docs-calibration.test.ts +11 -26
- package/mcp-server/src/relocation-integrity-gate/__tests__/relocation-integrity-gate.test.ts +72 -5
- package/mcp-server/src/relocation-integrity-gate/relocation-integrity-gate.ts +81 -24
- package/mcp-server/src/tools/list-cross-references.ts +2 -2
- package/package.json +24 -26
- package/src/__tests__/browser-distribution/css-bundling.test.ts +6 -4
- package/src/__tests__/console-allowlist.json +14 -0
- package/src/__tests__/console-fail-setup.ts +169 -0
- package/src/__tests__/integration/Spec107-DesignLanguageContext.test.ts +16 -0
- package/src/__tests__/stemma-system/behavioral-contract-validation.test.ts +70 -17
- package/src/__tests__/stemma-system/contract-catalog-name-validation.test.ts +28 -0
- package/src/__tests__/stemma-system/form-inputs-contracts.test.ts +223 -16
- package/src/__tests__/stemma-system/input-text-native-base-call-alignment.test.ts +298 -0
- package/src/blend/OklchBlendCalculator.ts +3 -0
- package/src/blend/ThemeAwareBlendUtilities.android.kt +3 -0
- package/src/blend/ThemeAwareBlendUtilities.ios.swift +3 -0
- package/src/blend/ThemeAwareBlendUtilities.web.ts +9 -1
- package/src/blend/__tests__/InteractionStateAudit.test.ts +12 -9
- package/src/build/errors/__tests__/ErrorHandler.integration.test.ts +8 -0
- package/src/build/errors/__tests__/ErrorHandler.test.ts +5 -0
- package/src/build/tokens/__tests__/defineComponentTokens.test.ts +113 -0
- package/src/build/tokens/defineComponentTokens.ts +43 -1
- package/src/build/workflow/__tests__/CICDIntegration.test.ts +12 -1
- package/src/cli/__tests__/init.test.ts +45 -11
- package/src/components/core/Avatar-Base/Avatar-Base.schema.yaml +1 -1
- package/src/components/core/Avatar-Base/__tests__/Avatar.accessibility.test.ts +121 -7
- package/src/components/core/Avatar-Base/__tests__/Avatar.image.test.ts +3 -0
- package/src/components/core/Avatar-Base/__tests__/Avatar.test.ts +15 -6
- package/src/components/core/Avatar-Base/avatar.tokens.ts +31 -34
- package/src/components/core/Avatar-Base/contracts.yaml +11 -1
- package/src/components/core/Avatar-Base/index.ts +1 -1
- package/src/components/core/Avatar-Base/platforms/web/Avatar.web.ts +24 -5
- package/src/components/core/Badge-Count-Base/contracts.yaml +1 -1
- package/src/components/core/Badge-Label-Base/contracts.yaml +1 -1
- package/src/components/core/Button-CTA/Button-CTA.schema.yaml +2 -12
- package/src/components/core/Button-CTA/README.md +3 -6
- package/src/components/core/Button-CTA/__tests__/ButtonCTA.test.ts +35 -89
- package/src/components/core/Button-CTA/__tests__/setup.test.ts +0 -2
- package/src/components/core/Button-CTA/__tests__/test-utils.ts +0 -2
- package/src/components/core/Button-CTA/contracts.yaml +6 -29
- package/src/components/core/Button-CTA/examples/BasicUsage.html +2 -14
- package/src/components/core/Button-CTA/examples/BasicUsage.tsx +17 -44
- package/src/components/core/Button-CTA/platforms/android/ButtonCTA.android.kt +12 -20
- package/src/components/core/Button-CTA/platforms/ios/ButtonCTA.ios.swift +12 -51
- package/src/components/core/Button-CTA/platforms/web/ButtonCTA.web.css +2 -26
- package/src/components/core/Button-CTA/platforms/web/ButtonCTA.web.ts +18 -71
- package/src/components/core/Button-CTA/types.ts +10 -28
- package/src/components/core/Button-Icon/buttonIcon.tokens.ts +43 -27
- package/src/components/core/Chip-Base/__tests__/ChipBase.test.ts +13 -0
- package/src/components/core/Chip-Filter/__tests__/ChipFilter.test.ts +13 -0
- package/src/components/core/Chip-Input/__tests__/ChipInput.test.ts +13 -0
- package/src/components/core/Input-Text-Base/Input-Text-Base.schema.yaml +30 -2
- package/src/components/core/Input-Text-Base/README.md +25 -2
- package/src/components/core/Input-Text-Base/__tests__/focusIndicators.test.ts +16 -15
- package/src/components/core/Input-Text-Base/contracts.yaml +90 -0
- package/src/components/core/Input-Text-Base/platforms/android/InputTextBase.android.kt +26 -12
- package/src/components/core/Input-Text-Base/platforms/ios/InputTextBase.ios.swift +195 -59
- package/src/components/core/Input-Text-Base/types.ts +13 -1
- package/src/components/core/Input-Text-Email/Input-Text-Email.schema.yaml +5 -1
- package/src/components/core/Input-Text-Email/README.md +8 -7
- package/src/components/core/Input-Text-Email/platforms/android/InputTextEmail.android.kt +1 -4
- package/src/components/core/Input-Text-Email/platforms/ios/InputTextEmail.ios.swift +2 -16
- package/src/components/core/Input-Text-Password/Input-Text-Password.schema.yaml +10 -3
- package/src/components/core/Input-Text-Password/README.md +9 -8
- package/src/components/core/Input-Text-Password/contracts.yaml +5 -0
- package/src/components/core/Input-Text-Password/platforms/android/InputTextPassword.android.kt +17 -7
- package/src/components/core/Input-Text-Password/platforms/ios/InputTextPassword.ios.swift +22 -20
- package/src/components/core/Input-Text-Password/platforms/web/InputTextPassword.web.ts +11 -2
- package/src/components/core/Input-Text-PhoneNumber/Input-Text-PhoneNumber.schema.yaml +5 -1
- package/src/components/core/Input-Text-PhoneNumber/README.md +9 -8
- package/src/components/core/Input-Text-PhoneNumber/platforms/android/InputTextPhoneNumber.android.kt +2 -5
- package/src/components/core/Input-Text-PhoneNumber/platforms/ios/InputTextPhoneNumber.ios.swift +3 -17
- package/src/components/core/Nav-Header-App/contracts.yaml +1 -1
- package/src/components/core/Nav-SegmentedChoice-Base/contracts.yaml +1 -1
- package/src/components/core/Progress-Indicator-Connector-Base/contracts.yaml +1 -1
- package/src/components/core/Progress-Indicator-Label-Base/contracts.yaml +1 -1
- package/src/components/core/Progress-Indicator-Node-Base/contracts.yaml +1 -1
- package/src/components/core/Progress-Stepper-Base/__tests__/StepperBase.test.ts +5 -2
- package/src/components/core/Progress-Stepper-Detailed/__tests__/StepperDetailed.test.ts +5 -2
- package/src/generators/DTCGFormatGenerator.ts +6 -0
- package/src/generators/TokenFileGenerator.ts +7 -2
- package/src/generators/__tests__/DTCGConfigOptions.test.ts +14 -5
- package/src/integration/BuildErrorHandler.ts +2 -2
- package/src/tokens/OpacityTokens.ts +1 -1
- package/src/tokens/__tests__/OpacityTokens.test.ts +3 -1
- package/src/tokens/__tests__/ProgressTokenCompliance.test.ts +5 -3
- package/src/tokens/__tests__/ProgressTokenFormulas.test.ts +11 -11
- package/src/tokens/__tests__/ProgressTokenTranslation.test.ts +22 -20
- package/src/tokens/component/progress.ts +83 -21
- package/src/tokens/semantic/BlendTokens.ts +26 -5
- package/src/tokens/semantic/OpacityTokens.ts +4 -4
- package/src/types/ComponentTypes.ts +1 -1
- package/src/types/generated/TokenTypes.ts +1 -1
- package/src/validators/StemmaTokenUsageValidator.ts +3 -2
- package/token-index/components.yaml +8 -8
- package/token-index/semantics.yaml +1 -2
- package/src/tools/release/__tests__/ChangeClassifier.test.ts +0 -133
- package/src/tools/release/__tests__/ChangeExtractor.test.ts +0 -222
- package/src/tools/release/__tests__/GitHubPublisher.test.ts +0 -240
- package/src/tools/release/__tests__/NotesRenderer.test.ts +0 -142
- package/src/tools/release/__tests__/NpmPublisher.test.ts +0 -289
- package/src/tools/release/__tests__/PipelineIntegration.test.ts +0 -188
- package/src/tools/release/__tests__/ReleasePipeline.test.ts +0 -192
- package/src/tools/release/__tests__/SemanticVersionValidator.test.ts +0 -49
- package/src/tools/release/__tests__/SummaryScanner.test.ts +0 -141
- package/src/tools/release/__tests__/TagResolver.test.ts +0 -91
- package/src/tools/release/__tests__/VersionCalculator.test.ts +0 -270
- package/src/tools/release/__tests__/helpers/NpmMockHelper.ts +0 -80
- package/src/tools/release/cli/ReleasePipeline.ts +0 -165
- package/src/tools/release/cli/release-tool.ts +0 -107
- package/src/tools/release/pipeline/ChangeClassifier.ts +0 -61
- package/src/tools/release/pipeline/ChangeExtractor.ts +0 -87
- package/src/tools/release/pipeline/NotesRenderer.ts +0 -66
- package/src/tools/release/pipeline/SummaryScanner.ts +0 -70
- package/src/tools/release/pipeline/TagResolver.ts +0 -40
- package/src/tools/release/pipeline/VersionCalculator.ts +0 -375
- package/src/tools/release/publishers/GitHubPublisher.ts +0 -228
- package/src/tools/release/publishers/NpmPublisher.ts +0 -196
- package/src/tools/release/release-config.json +0 -5
- package/src/tools/release/types/index.ts +0 -282
- package/src/tools/release/validators/SemanticVersionValidator.ts +0 -67
|
@@ -23,11 +23,45 @@ import * as fs from 'fs';
|
|
|
23
23
|
import * as os from 'os';
|
|
24
24
|
import * as path from 'path';
|
|
25
25
|
import { runInit } from '../init';
|
|
26
|
+
import { resolvePackageRoot } from '../shared/resolvePackageRoot';
|
|
26
27
|
|
|
27
28
|
// ---------------------------------------------------------------------------
|
|
28
29
|
// Helpers
|
|
29
30
|
// ---------------------------------------------------------------------------
|
|
30
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Count files recursively under `dir`, matching `copyDir()`'s counting semantics
|
|
34
|
+
* in init.ts (files only, descending into subdirectories).
|
|
35
|
+
*
|
|
36
|
+
* Used to DERIVE the expected governance-doc count from the same source `init`
|
|
37
|
+
* copies from, rather than hard-coding a literal. The governance corpus is the
|
|
38
|
+
* relocated non-identity docs (Spec 119-A) and grows by design — a hard-coded
|
|
39
|
+
* count drifts (and repeatedly HAS drifted, e.g. 80 → 81 → 82) every time a doc
|
|
40
|
+
* is added, with no relation to the behavior under test. Deriving from source
|
|
41
|
+
* keeps the real check — "init copies EVERY governance doc, dropping none" —
|
|
42
|
+
* while decoupling it from the corpus's exact size.
|
|
43
|
+
*/
|
|
44
|
+
function countFilesRecursive(dir: string): number {
|
|
45
|
+
let count = 0;
|
|
46
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
47
|
+
count += entry.isDirectory()
|
|
48
|
+
? countFilesRecursive(path.join(dir, entry.name))
|
|
49
|
+
: entry.isFile()
|
|
50
|
+
? 1
|
|
51
|
+
: 0;
|
|
52
|
+
}
|
|
53
|
+
return count;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// The package root `init` copies from is resolved by `resolvePackageRoot(<init's
|
|
57
|
+
// dir>)` = two levels up from `src/cli/`. This test file lives one level deeper
|
|
58
|
+
// (`src/cli/__tests__/`), so pass its parent (`src/cli/`) to resolve the SAME root.
|
|
59
|
+
const PKG_ROOT = resolvePackageRoot(path.join(__dirname, '..'));
|
|
60
|
+
|
|
61
|
+
// Expected governance-doc count, derived from source (see countFilesRecursive).
|
|
62
|
+
// NOT hard-coded: governance/ grows as the corpus grows.
|
|
63
|
+
const GOVERNANCE_DOC_COUNT = countFilesRecursive(path.join(PKG_ROOT, 'governance'));
|
|
64
|
+
|
|
31
65
|
/**
|
|
32
66
|
* Create a unique scratch directory under the OS temp dir.
|
|
33
67
|
* Returns the absolute path. Caller is responsible for cleanup.
|
|
@@ -140,11 +174,12 @@ describe('CLI init — integration', () => {
|
|
|
140
174
|
expect(secondOutput).toContain('existing files preserved');
|
|
141
175
|
// Spec 119-A two-root split: 9 identity docs ship in .kiro/steering/
|
|
142
176
|
// (8 identity + the NEW Task-Completion-Protocol added in Task 8; the
|
|
143
|
-
// meta-guide was removed in 119-A Task 10.5 → back to 9)
|
|
144
|
-
//
|
|
145
|
-
//
|
|
177
|
+
// meta-guide was removed in 119-A Task 10.5 → back to 9). The identity set
|
|
178
|
+
// is a LOCKED always-set — asserted as exactly 9 on purpose, so a change to
|
|
179
|
+
// it is a signal worth catching. The governance corpus, by contrast, grows
|
|
180
|
+
// routinely, so its count is derived from source (GOVERNANCE_DOC_COUNT).
|
|
146
181
|
expect(secondOutput).toContain('✓ steering docs: 9 existing files preserved');
|
|
147
|
-
expect(secondOutput).toContain(
|
|
182
|
+
expect(secondOutput).toContain(`✓ governance docs: ${GOVERNANCE_DOC_COUNT} existing files preserved`);
|
|
148
183
|
});
|
|
149
184
|
|
|
150
185
|
test('preserves existing files — no overwrites after second run', async () => {
|
|
@@ -190,11 +225,11 @@ describe('CLI init — integration', () => {
|
|
|
190
225
|
// Spec 119-A two-root split: the package contributes 9 identity steering
|
|
191
226
|
// files (8 identity + the NEW Task-Completion-Protocol from Task 8; the
|
|
192
227
|
// meta-guide was removed in Task 10.5; no conflict with designerpunk.md
|
|
193
|
-
// because the package doesn't have a file by that name) and
|
|
194
|
-
// docs into the separate governance/ dir
|
|
195
|
-
//
|
|
228
|
+
// because the package doesn't have a file by that name) and the relocated
|
|
229
|
+
// non-identity docs into the separate governance/ dir. Steering is the
|
|
230
|
+
// locked 9; governance is derived from source (grows with the corpus).
|
|
196
231
|
expect(output).toContain('✓ steering docs: 9 new files');
|
|
197
|
-
expect(output).toContain(
|
|
232
|
+
expect(output).toContain(`✓ governance docs: ${GOVERNANCE_DOC_COUNT} new files`);
|
|
198
233
|
|
|
199
234
|
// Custom file preserved
|
|
200
235
|
expect(
|
|
@@ -205,10 +240,9 @@ describe('CLI init — integration', () => {
|
|
|
205
240
|
const steeringFiles = fs.readdirSync(path.join(scratchDir, '.kiro/steering'));
|
|
206
241
|
expect(steeringFiles.length).toBe(10);
|
|
207
242
|
|
|
208
|
-
// The
|
|
209
|
-
// (80 + Steering-Addressing-Conventions.md added in Task 12)
|
|
243
|
+
// The relocated docs land in governance/ — every one init copied from source.
|
|
210
244
|
const governanceFiles = fs.readdirSync(path.join(scratchDir, 'governance'));
|
|
211
|
-
expect(governanceFiles.length).toBe(
|
|
245
|
+
expect(governanceFiles.length).toBe(GOVERNANCE_DOC_COUNT);
|
|
212
246
|
});
|
|
213
247
|
});
|
|
214
248
|
|
|
@@ -60,7 +60,7 @@ properties:
|
|
|
60
60
|
alt:
|
|
61
61
|
type: string
|
|
62
62
|
required: false
|
|
63
|
-
description: Alt text for accessibility. Required if src provided.
|
|
63
|
+
description: Alt text for accessibility. Required if src provided on human type, unless decorative is true (agent type ignores src entirely, per Req 5 AC 5).
|
|
64
64
|
interactive:
|
|
65
65
|
type: boolean
|
|
66
66
|
required: false
|
|
@@ -212,14 +212,20 @@ describe('Avatar Component Accessibility', () => {
|
|
|
212
212
|
});
|
|
213
213
|
|
|
214
214
|
it('should apply empty alt when alt is empty string', async () => {
|
|
215
|
+
// Spec 126 (O2): uses the raw-attribute form (setAttribute) to produce a
|
|
216
|
+
// genuine alt="" — the `alt` *property* setter treats '' as falsy and calls
|
|
217
|
+
// removeAttribute, collapsing `avatar.alt = ''` to the same "absent" state as
|
|
218
|
+
// omitting alt entirely (see Avatar.web.ts `set alt`). Only the attribute form
|
|
219
|
+
// yields a real empty-string alt, and under the settled warn condition
|
|
220
|
+
// (`alt == null`), a real empty-string alt does not warn — so no spy is needed.
|
|
215
221
|
const avatar = document.createElement('avatar-base') as AvatarBaseElement;
|
|
216
222
|
avatar.type = 'human';
|
|
217
223
|
avatar.src = 'https://example.com/profile.jpg';
|
|
218
|
-
avatar.alt
|
|
224
|
+
avatar.setAttribute('alt', '');
|
|
219
225
|
document.body.appendChild(avatar);
|
|
220
|
-
|
|
226
|
+
|
|
221
227
|
await new Promise(resolve => setTimeout(resolve, 0));
|
|
222
|
-
|
|
228
|
+
|
|
223
229
|
const img = avatar.shadowRoot?.querySelector('.avatar__image');
|
|
224
230
|
expect(img).toBeTruthy();
|
|
225
231
|
expect(img?.getAttribute('alt')).toBe('');
|
|
@@ -285,16 +291,124 @@ describe('Avatar Component Accessibility', () => {
|
|
|
285
291
|
|
|
286
292
|
it('should NOT warn when no src is provided', async () => {
|
|
287
293
|
const consoleSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
|
|
288
|
-
|
|
294
|
+
|
|
289
295
|
const avatar = document.createElement('avatar-base') as AvatarBaseElement;
|
|
290
296
|
avatar.type = 'human';
|
|
291
297
|
// No src, no alt
|
|
292
298
|
document.body.appendChild(avatar);
|
|
293
|
-
|
|
299
|
+
|
|
294
300
|
await new Promise(resolve => setTimeout(resolve, 0));
|
|
295
|
-
|
|
301
|
+
|
|
296
302
|
expect(consoleSpy).not.toHaveBeenCalled();
|
|
297
|
-
|
|
303
|
+
|
|
304
|
+
consoleSpy.mockRestore();
|
|
305
|
+
});
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
// ============================================================================
|
|
309
|
+
// Alt Text Warning — Decorative & Type-Scope Edge Semantics (Spec 126, O2)
|
|
310
|
+
// ============================================================================
|
|
311
|
+
//
|
|
312
|
+
// Settled warn condition: `type === 'human' && src && alt == null && !decorative`
|
|
313
|
+
// @see Requirements: 5.4 - Human type + src SHALL require alt for accessibility
|
|
314
|
+
// @see Requirements: 5 AC 5 - Agent type SHALL ignore src entirely
|
|
315
|
+
// @see .kiro/specs/126-avatar-decorative-warn/design-outline.md § 4/5
|
|
316
|
+
//
|
|
317
|
+
// Together with the sentinel test above ('should warn when src is provided
|
|
318
|
+
// without alt'), this block forms the six-test matrix settled by Spec 126 § 5
|
|
319
|
+
// item 2. The sentinel is left unchanged (Spec 126 § 7 non-regression signal).
|
|
320
|
+
describe('Alt Text Warning — Decorative & Type-Scope Edge Semantics (Spec 126)', () => {
|
|
321
|
+
it('should NOT warn when alt="" is set via raw attribute (explicit empty, not "absent")', async () => {
|
|
322
|
+
const consoleSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
|
|
323
|
+
|
|
324
|
+
const avatar = document.createElement('avatar-base') as AvatarBaseElement;
|
|
325
|
+
avatar.type = 'human';
|
|
326
|
+
avatar.src = 'https://example.com/profile.jpg';
|
|
327
|
+
// Raw attribute form is the only path that yields a genuine alt="" (getter
|
|
328
|
+
// returns '', not null) — the `alt` property setter collapses falsy values to
|
|
329
|
+
// removeAttribute, so this test must use setAttribute to exercise the real case.
|
|
330
|
+
avatar.setAttribute('alt', '');
|
|
331
|
+
document.body.appendChild(avatar);
|
|
332
|
+
|
|
333
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
334
|
+
|
|
335
|
+
expect(consoleSpy).not.toHaveBeenCalled();
|
|
336
|
+
|
|
337
|
+
consoleSpy.mockRestore();
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
it('should warn when alt is set to empty string via the property (collapses to null via the setter)', async () => {
|
|
341
|
+
const consoleSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
|
|
342
|
+
|
|
343
|
+
const avatar = document.createElement('avatar-base') as AvatarBaseElement;
|
|
344
|
+
avatar.type = 'human';
|
|
345
|
+
avatar.src = 'https://example.com/profile.jpg';
|
|
346
|
+
// Property-form empty string: the `alt` setter treats '' as falsy and calls
|
|
347
|
+
// removeAttribute('alt'), so this lands in the "alt absent" bucket and warns —
|
|
348
|
+
// pins the attribute-vs-property collapse explicitly (Spec 126 § 5 item 2).
|
|
349
|
+
avatar.alt = '';
|
|
350
|
+
document.body.appendChild(avatar);
|
|
351
|
+
|
|
352
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
353
|
+
|
|
354
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
355
|
+
expect.stringContaining('alt')
|
|
356
|
+
);
|
|
357
|
+
|
|
358
|
+
consoleSpy.mockRestore();
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
it('should NOT warn when decorative is true, even without alt (co-asserted with aria-hidden)', async () => {
|
|
362
|
+
const consoleSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
|
|
363
|
+
|
|
364
|
+
const avatar = document.createElement('avatar-base') as AvatarBaseElement;
|
|
365
|
+
avatar.type = 'human';
|
|
366
|
+
avatar.src = 'https://example.com/profile.jpg';
|
|
367
|
+
avatar.decorative = true;
|
|
368
|
+
// No alt provided
|
|
369
|
+
document.body.appendChild(avatar);
|
|
370
|
+
|
|
371
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
372
|
+
|
|
373
|
+
expect(consoleSpy).not.toHaveBeenCalled();
|
|
374
|
+
|
|
375
|
+
// Co-assert the semantic linkage (Req 9.2): the warn is correctly suppressed
|
|
376
|
+
// because the avatar is genuinely hidden from screen readers, not incidentally.
|
|
377
|
+
const container = avatar.shadowRoot?.querySelector('.avatar');
|
|
378
|
+
expect(container?.getAttribute('aria-hidden')).toBe('true');
|
|
379
|
+
|
|
380
|
+
consoleSpy.mockRestore();
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
it('should NOT warn for agent type with src and no alt (Req 5 AC 5 — agent ignores src entirely)', async () => {
|
|
384
|
+
const consoleSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
|
|
385
|
+
|
|
386
|
+
const avatar = document.createElement('avatar-base') as AvatarBaseElement;
|
|
387
|
+
avatar.type = 'agent';
|
|
388
|
+
avatar.src = 'https://example.com/profile.jpg';
|
|
389
|
+
// No alt provided
|
|
390
|
+
document.body.appendChild(avatar);
|
|
391
|
+
|
|
392
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
393
|
+
|
|
394
|
+
expect(consoleSpy).not.toHaveBeenCalled();
|
|
395
|
+
|
|
396
|
+
consoleSpy.mockRestore();
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
it('should NOT warn for agent type with src and alt both present (confirms silencing is type-driven, not alt-driven)', async () => {
|
|
400
|
+
const consoleSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
|
|
401
|
+
|
|
402
|
+
const avatar = document.createElement('avatar-base') as AvatarBaseElement;
|
|
403
|
+
avatar.type = 'agent';
|
|
404
|
+
avatar.src = 'https://example.com/profile.jpg';
|
|
405
|
+
avatar.alt = 'Ignored for agent type';
|
|
406
|
+
document.body.appendChild(avatar);
|
|
407
|
+
|
|
408
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
409
|
+
|
|
410
|
+
expect(consoleSpy).not.toHaveBeenCalled();
|
|
411
|
+
|
|
298
412
|
consoleSpy.mockRestore();
|
|
299
413
|
});
|
|
300
414
|
});
|
|
@@ -287,6 +287,7 @@ describe('Avatar Component Image Handling', () => {
|
|
|
287
287
|
const avatar = document.createElement('avatar-base') as AvatarBaseElement;
|
|
288
288
|
avatar.type = 'agent';
|
|
289
289
|
avatar.src = 'https://example.com/agent-image.jpg';
|
|
290
|
+
avatar.alt = 'Agent profile'; // honor the alt-with-src contract (Req 5.4)
|
|
290
291
|
document.body.appendChild(avatar);
|
|
291
292
|
|
|
292
293
|
await new Promise(resolve => setTimeout(resolve, 0));
|
|
@@ -299,6 +300,7 @@ describe('Avatar Component Image Handling', () => {
|
|
|
299
300
|
const avatar = document.createElement('avatar-base') as AvatarBaseElement;
|
|
300
301
|
avatar.type = 'agent';
|
|
301
302
|
avatar.src = 'https://example.com/agent-image.jpg';
|
|
303
|
+
avatar.alt = 'Agent profile'; // honor the alt-with-src contract (Req 5.4)
|
|
302
304
|
document.body.appendChild(avatar);
|
|
303
305
|
|
|
304
306
|
await new Promise(resolve => setTimeout(resolve, 0));
|
|
@@ -311,6 +313,7 @@ describe('Avatar Component Image Handling', () => {
|
|
|
311
313
|
const avatar = document.createElement('avatar-base') as AvatarBaseElement;
|
|
312
314
|
avatar.type = 'agent';
|
|
313
315
|
avatar.src = 'https://example.com/agent-image.jpg';
|
|
316
|
+
avatar.alt = 'Agent profile'; // honor the alt-with-src contract (Req 5.4)
|
|
314
317
|
document.body.appendChild(avatar);
|
|
315
318
|
|
|
316
319
|
await new Promise(resolve => setTimeout(resolve, 0));
|
|
@@ -218,27 +218,33 @@ describe('Avatar Component Core API', () => {
|
|
|
218
218
|
// ============================================================================
|
|
219
219
|
|
|
220
220
|
describe('Src Prop', () => {
|
|
221
|
+
// Fixtures set `alt` alongside `src` so they honor the component's own a11y
|
|
222
|
+
// contract (Req 5.4: alt is required with src) — otherwise the component
|
|
223
|
+
// rightly warns, polluting CI logs. The assertions below are about src only.
|
|
221
224
|
it('should accept src value', () => {
|
|
222
225
|
const avatar = document.createElement('avatar-base') as AvatarBaseElement;
|
|
223
226
|
avatar.src = 'https://example.com/image.jpg';
|
|
227
|
+
avatar.alt = 'Test avatar';
|
|
224
228
|
document.body.appendChild(avatar);
|
|
225
|
-
|
|
229
|
+
|
|
226
230
|
expect(avatar.src).toBe('https://example.com/image.jpg');
|
|
227
231
|
expect(avatar.getAttribute('src')).toBe('https://example.com/image.jpg');
|
|
228
232
|
});
|
|
229
233
|
|
|
230
234
|
it('should update src via attribute', () => {
|
|
231
235
|
const avatar = document.createElement('avatar-base') as AvatarBaseElement;
|
|
236
|
+
avatar.alt = 'Test avatar';
|
|
232
237
|
document.body.appendChild(avatar);
|
|
233
|
-
|
|
238
|
+
|
|
234
239
|
avatar.setAttribute('src', 'https://example.com/new-image.jpg');
|
|
235
240
|
expect(avatar.src).toBe('https://example.com/new-image.jpg');
|
|
236
241
|
});
|
|
237
242
|
|
|
238
243
|
it('should update src via property', () => {
|
|
239
244
|
const avatar = document.createElement('avatar-base') as AvatarBaseElement;
|
|
245
|
+
avatar.alt = 'Test avatar';
|
|
240
246
|
document.body.appendChild(avatar);
|
|
241
|
-
|
|
247
|
+
|
|
242
248
|
avatar.src = 'https://example.com/profile.png';
|
|
243
249
|
expect(avatar.getAttribute('src')).toBe('https://example.com/profile.png');
|
|
244
250
|
});
|
|
@@ -246,8 +252,9 @@ describe('Avatar Component Core API', () => {
|
|
|
246
252
|
it('should remove src attribute when set to null', () => {
|
|
247
253
|
const avatar = document.createElement('avatar-base') as AvatarBaseElement;
|
|
248
254
|
avatar.src = 'https://example.com/image.jpg';
|
|
255
|
+
avatar.alt = 'Test avatar';
|
|
249
256
|
document.body.appendChild(avatar);
|
|
250
|
-
|
|
257
|
+
|
|
251
258
|
avatar.src = null;
|
|
252
259
|
expect(avatar.src).toBeNull();
|
|
253
260
|
expect(avatar.hasAttribute('src')).toBe(false);
|
|
@@ -566,8 +573,9 @@ describe('Avatar Component Core API', () => {
|
|
|
566
573
|
|
|
567
574
|
it('should reflect src property to attribute', () => {
|
|
568
575
|
const avatar = document.createElement('avatar-base') as AvatarBaseElement;
|
|
576
|
+
avatar.alt = 'Test avatar'; // honor the alt-with-src contract (Req 5.4)
|
|
569
577
|
document.body.appendChild(avatar);
|
|
570
|
-
|
|
578
|
+
|
|
571
579
|
avatar.src = 'https://example.com/test.jpg';
|
|
572
580
|
expect(avatar.getAttribute('src')).toBe('https://example.com/test.jpg');
|
|
573
581
|
});
|
|
@@ -629,8 +637,9 @@ describe('Avatar Component Core API', () => {
|
|
|
629
637
|
it('should read src attribute as property', () => {
|
|
630
638
|
const avatar = document.createElement('avatar-base') as AvatarBaseElement;
|
|
631
639
|
avatar.setAttribute('src', 'https://example.com/img.png');
|
|
640
|
+
avatar.setAttribute('alt', 'Test avatar'); // honor the alt-with-src contract (Req 5.4)
|
|
632
641
|
document.body.appendChild(avatar);
|
|
633
|
-
|
|
642
|
+
|
|
634
643
|
expect(avatar.src).toBe('https://example.com/img.png');
|
|
635
644
|
});
|
|
636
645
|
|
|
@@ -33,7 +33,16 @@
|
|
|
33
33
|
* Icon Size Derivations (in Avatar.web.ts):
|
|
34
34
|
* - xs: calc(icon.size050 × 0.75) = 12px
|
|
35
35
|
* - xxl: calc(icon.size050 × 4) = 64px
|
|
36
|
-
*
|
|
36
|
+
*
|
|
37
|
+
* TOKEN FAMILY: all dimensional Avatar tokens (container `size.*` AND icon
|
|
38
|
+
* `icon.size.*` gap fillers) are registered by the single sizing-family call
|
|
39
|
+
* `AvatarSizingTokens` below. The `icon.size.*` pair previously lived in a
|
|
40
|
+
* separate `AvatarTokens` call stamped `family: 'spacing'` — a mislabel, since
|
|
41
|
+
* both are dimensional sizing values. `AvatarTokens` is removed; consumers use
|
|
42
|
+
* `AvatarSizingTokens` or the `getAvatarIconSize()` / `getAvatarSize()` accessors.
|
|
43
|
+
* Generated platform output is unaffected: both calls declare `component: 'Avatar'`,
|
|
44
|
+
* so all tokens still land in one `AvatarTokens` Swift enum / Kotlin object.
|
|
45
|
+
*
|
|
37
46
|
* COLOR TOKENS (Spec 058):
|
|
38
47
|
* Avatar color tokens are defined in this file following the Rosetta System architecture
|
|
39
48
|
* which mandates component tokens live at src/components/[ComponentName]/tokens.ts.
|
|
@@ -56,13 +65,24 @@ import { defineComponentTokens } from '../../../build/tokens';
|
|
|
56
65
|
import { SIZING_BASE_VALUE, sizingTokens } from '../../../tokens/SizingTokens';
|
|
57
66
|
|
|
58
67
|
/**
|
|
59
|
-
* Avatar sizing tokens — container dimensions for each size variant.
|
|
68
|
+
* Avatar sizing tokens — container dimensions and icon dimensions for each size variant.
|
|
60
69
|
*
|
|
61
70
|
* Previously in a separate file (avatar-sizing.tokens.ts). Inlined to prevent
|
|
62
71
|
* architectural anomaly (no other component splits tokens across files) and
|
|
63
72
|
* simplify the package surface for sync.
|
|
64
73
|
*
|
|
74
|
+
* ONE CALL PER FAMILY. Every token registered by a single defineComponentTokens()
|
|
75
|
+
* call is stamped with that call's `family`, and the family drives platform output
|
|
76
|
+
* (Swift type, Kotlin `.dp` suffix, primitive class name). The `icon.size.*` gap
|
|
77
|
+
* fillers below are dimensional sizing values, so they live in this sizing-family
|
|
78
|
+
* call — they were previously mis-stamped `family: 'spacing'` in a separate
|
|
79
|
+
* `AvatarTokens` call, the same latent mislabel class fixed for Button-Icon.
|
|
80
|
+
* Do NOT spread/merge this result with another call's result — the rich metadata
|
|
81
|
+
* rides on a non-enumerable brand (see src/build/tokens/defineComponentTokens.ts,
|
|
82
|
+
* TOKEN_CONTRACT_BRAND) that a spread would silently drop.
|
|
83
|
+
*
|
|
65
84
|
* @see .kiro/specs/092-sizing-token-family/design.md
|
|
85
|
+
* @see src/components/core/Button-Icon/buttonIcon.tokens.ts for the two-call pattern
|
|
66
86
|
*/
|
|
67
87
|
export const AvatarSizingTokens = defineComponentTokens({
|
|
68
88
|
component: 'Avatar',
|
|
@@ -92,38 +112,15 @@ export const AvatarSizingTokens = defineComponentTokens({
|
|
|
92
112
|
reference: sizingTokens.size1600,
|
|
93
113
|
reasoning: 'Extra extra large avatar (128px). Full profile view, onboarding.',
|
|
94
114
|
},
|
|
95
|
-
},
|
|
96
|
-
});
|
|
97
115
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
*
|
|
107
|
-
* Size token values:
|
|
108
|
-
* - size.xs: 24px (3 × base, references size300)
|
|
109
|
-
* - size.sm: 32px (4 × base, references size400)
|
|
110
|
-
* - size.md: 40px (5 × base, references size500)
|
|
111
|
-
* - size.lg: 48px (6 × base, references size600)
|
|
112
|
-
* - size.xl: 80px (10 × base, derivation)
|
|
113
|
-
* - size.xxl: 128px (16 × base, derivation)
|
|
114
|
-
*
|
|
115
|
-
* Icon size token values (gap fillers - web uses calc() instead):
|
|
116
|
-
* - icon.size.xs: 12px (1.5 × base, derivation) - web uses calc(icon.size050 × 0.75)
|
|
117
|
-
* - icon.size.xxl: 64px (8 × base, derivation) - web uses calc(icon.size050 × 4)
|
|
118
|
-
*
|
|
119
|
-
* @see Requirements 2.1-2.6, 3.1, 3.6 in .kiro/specs/042-avatar-component/requirements.md
|
|
120
|
-
*/
|
|
121
|
-
export const AvatarTokens = defineComponentTokens({
|
|
122
|
-
component: 'Avatar',
|
|
123
|
-
family: 'spacing',
|
|
124
|
-
tokens: {
|
|
125
|
-
// Icon size tokens (gap fillers for sizes without existing icon tokens)
|
|
126
|
-
// These fill gaps where no standard icon token exists at the required 50% ratio
|
|
116
|
+
// Icon size tokens (gap fillers for sizes without an existing icon token).
|
|
117
|
+
// Kept on the VALUE path deliberately: sizing primitives exist at both values
|
|
118
|
+
// (size150 = 12, size800 = 64), but the reference path currently emits a
|
|
119
|
+
// fabricated `SizingTokens.<name>` class on iOS/Android that no generated or
|
|
120
|
+
// hand-written platform file defines. Switching these to `reference:` would
|
|
121
|
+
// trade compiling output (`12.dp`) for non-compiling output and break
|
|
122
|
+
// Avatar.android.kt's `val iconSizeXs: Dp = GeneratedAvatarTokens.iconSizeXs`.
|
|
123
|
+
// Revisit once TokenFileGenerator.getFamilyClassName emits a real platform type.
|
|
127
124
|
'icon.size.xs': {
|
|
128
125
|
value: SIZING_BASE_VALUE * 1.5,
|
|
129
126
|
reasoning: 'Icon size for xs avatar (12px = 1.5× base) maintains 50% ratio (12/24). No existing icon token at this size, so component token fills the gap.',
|
|
@@ -273,7 +270,7 @@ export function getAvatarSize(variant: AvatarSizeVariant): number {
|
|
|
273
270
|
* @see Requirements 3.1, 3.6 in .kiro/specs/042-avatar-component/requirements.md
|
|
274
271
|
*/
|
|
275
272
|
export function getAvatarIconSize(variant: AvatarIconSizeVariant): number {
|
|
276
|
-
return
|
|
273
|
+
return AvatarSizingTokens[`icon.size.${variant}`];
|
|
277
274
|
}
|
|
278
275
|
|
|
279
276
|
/**
|
|
@@ -162,7 +162,17 @@ contracts:
|
|
|
162
162
|
When not decorative, Avatar provides accessible name:
|
|
163
163
|
- If alt prop provided: uses alt text
|
|
164
164
|
- If no alt prop (iOS/Android): "User avatar" for human, "AI agent avatar" for agent
|
|
165
|
-
- Web requires alt prop when src is provided
|
|
165
|
+
- Web requires alt prop when src is provided, scoped to human type only
|
|
166
|
+
(agent type ignores src entirely — Req 5 AC 5) and only when decorative
|
|
167
|
+
is false (decorative is the component's own non-informative signal,
|
|
168
|
+
Req 9.2, and takes precedence over the alt requirement)
|
|
169
|
+
- Web edge semantics (settled Spec 126, O2, ratified 2026-07-09): the dev
|
|
170
|
+
warn fires iff `type === 'human' && src && alt == null && !decorative`.
|
|
171
|
+
A raw explicit empty alt (`setAttribute('alt', '')`) does not warn —
|
|
172
|
+
it is treated as the deliberate empty-alt signal. The `alt` property
|
|
173
|
+
setter collapses a falsy value (including '') to removeAttribute, so
|
|
174
|
+
setting `avatar.alt = ''` via the property lands in the "absent" bucket
|
|
175
|
+
and DOES warn — this is a setter-collapse detail, not a design gap.
|
|
166
176
|
wcag: "1.1.1 Non-text Content"
|
|
167
177
|
platforms: [web, ios, android]
|
|
168
178
|
validation:
|
|
@@ -700,12 +700,31 @@ export class AvatarBaseElement extends HTMLElement {
|
|
|
700
700
|
// @see Requirements: 9.2 - Apply aria-hidden="true" when decorative prop is true
|
|
701
701
|
const ariaHiddenAttr = decorative ? ' aria-hidden="true"' : '';
|
|
702
702
|
|
|
703
|
-
// Warn in development if
|
|
704
|
-
// @see Requirements: 5.4 -
|
|
705
|
-
|
|
703
|
+
// Warn in development if a human-type avatar is missing alt text for its image.
|
|
704
|
+
// @see Requirements: 5.4 - Human type + src SHALL require alt for accessibility
|
|
705
|
+
// @see Requirements: 5 AC 5 - Agent type SHALL ignore src entirely (src not
|
|
706
|
+
// rendered as an image), so agent-type avatars never warn regardless of alt.
|
|
707
|
+
//
|
|
708
|
+
// Settled edge semantics (Spec 126, O2, ratified 2026-07-09):
|
|
709
|
+
// - Scope is human-type only — Req 5.4 is human-type-scoped, and Req 5 AC 5
|
|
710
|
+
// affirmatively establishes that agent-type SHALL ignore src, so a src+no-alt
|
|
711
|
+
// agent avatar is not a violation of anything.
|
|
712
|
+
// - `alt == null` (loose equality — also excludes `undefined`), not `!alt`, is the
|
|
713
|
+
// test. The `alt` attribute getter returns `getAttribute('alt')`. Raw
|
|
714
|
+
// `setAttribute('alt', '')` yields `alt === ''`, which is the HTML-idiomatic
|
|
715
|
+
// explicit-empty-alt signal and must NOT warn. The `alt` *property setter*
|
|
716
|
+
// (see below) treats a falsy value as "remove the attribute," so
|
|
717
|
+
// `avatar.alt = ''` collapses to `alt === null` — indistinguishable from
|
|
718
|
+
// omitting alt entirely, and DOES warn under this condition.
|
|
719
|
+
// - `!decorative` is the operative fix for the confirmed false positive: a
|
|
720
|
+
// `decorative` avatar is the component's official signal that the image is
|
|
721
|
+
// non-informative (Req 9.2 applies `aria-hidden="true"`), so it must never warn
|
|
722
|
+
// regardless of alt.
|
|
723
|
+
if (type === 'human' && src && alt == null && !decorative && typeof console !== 'undefined') {
|
|
706
724
|
console.warn(
|
|
707
|
-
'AvatarBaseElement: "alt"
|
|
708
|
-
'
|
|
725
|
+
'AvatarBaseElement: "alt" is required when "src" is provided. ' +
|
|
726
|
+
'Add an alt description of the image; or, if this avatar is purely decorative ' +
|
|
727
|
+
'(non-informative), set the "decorative" prop to hide it from screen readers.'
|
|
709
728
|
);
|
|
710
729
|
}
|
|
711
730
|
|
|
@@ -102,7 +102,7 @@ contracts:
|
|
|
102
102
|
- No hover or press visual feedback
|
|
103
103
|
- pointer-events: none (web)
|
|
104
104
|
- No gesture handlers (native)
|
|
105
|
-
wcag: null
|
|
105
|
+
wcag: "N/A" # legitimate-null: non-interactive negative guarantee, no positive WCAG SC (125-B 4.2)
|
|
106
106
|
platforms: [web, ios, android]
|
|
107
107
|
validation:
|
|
108
108
|
- Badge cannot receive keyboard focus
|
|
@@ -77,7 +77,7 @@ contracts:
|
|
|
77
77
|
- No hover or press visual feedback
|
|
78
78
|
- pointer-events: none (web)
|
|
79
79
|
- No gesture handlers (native)
|
|
80
|
-
wcag: null
|
|
80
|
+
wcag: "N/A" # legitimate-null: non-interactive negative guarantee, no positive WCAG SC (125-B 4.2)
|
|
81
81
|
platforms: [web, ios, android]
|
|
82
82
|
validation:
|
|
83
83
|
- Badge cannot receive keyboard focus
|
|
@@ -90,20 +90,12 @@ properties:
|
|
|
90
90
|
When true, text is truncated with ellipsis instead of wrapping.
|
|
91
91
|
Default (false) allows text wrapping for accessibility and i18n support.
|
|
92
92
|
|
|
93
|
-
disabled:
|
|
94
|
-
type: boolean
|
|
95
|
-
required: false
|
|
96
|
-
default: false
|
|
97
|
-
description: |
|
|
98
|
-
When true, button is non-interactive with disabled visual styling.
|
|
99
|
-
Disabled buttons are not keyboard focusable and onPress is not called.
|
|
100
|
-
|
|
101
93
|
onPress:
|
|
102
94
|
type: "() => void"
|
|
103
95
|
required: true
|
|
104
96
|
description: |
|
|
105
97
|
Callback function invoked when button is clicked, tapped, or activated
|
|
106
|
-
via keyboard (Enter/Space).
|
|
98
|
+
via keyboard (Enter/Space).
|
|
107
99
|
|
|
108
100
|
testID:
|
|
109
101
|
type: string
|
|
@@ -153,7 +145,6 @@ tokens:
|
|
|
153
145
|
blend:
|
|
154
146
|
- blend.hoverDarker
|
|
155
147
|
- blend.pressedDarker
|
|
156
|
-
- blend.disabledDesaturate
|
|
157
148
|
- blend.iconLighter
|
|
158
149
|
component:
|
|
159
150
|
- buttonCTA.minWidth.small
|
|
@@ -247,11 +238,10 @@ accessibility:
|
|
|
247
238
|
- "1.4.11 Non-text Contrast - Focus ring meets 3:1 contrast"
|
|
248
239
|
- "2.1.1 Keyboard - Fully keyboard accessible (Tab, Enter, Space)"
|
|
249
240
|
- "2.4.7 Focus Visible - Clear focus indicator with :focus-visible"
|
|
250
|
-
- "4.1.2 Name, Role, Value - Proper ARIA attributes
|
|
241
|
+
- "4.1.2 Name, Role, Value - Proper ARIA attributes"
|
|
251
242
|
- "2.5.5 Target Size - Touch targets meet 44px minimum"
|
|
252
243
|
aria_attributes:
|
|
253
244
|
- role: button (implicit from semantic element)
|
|
254
|
-
- aria-disabled: true when disabled
|
|
255
245
|
- aria-label: label prop value
|
|
256
246
|
keyboard_navigation:
|
|
257
247
|
- Tab: Move focus to/from button
|
|
@@ -41,10 +41,11 @@ This component guarantees 7 behavioral contracts across all platforms:
|
|
|
41
41
|
| `pressable` | Responds to press/click events | web, ios, android | 2.1.1 |
|
|
42
42
|
| `hover_state` | Visual feedback on hover | web | 1.4.13 |
|
|
43
43
|
| `pressed_state` | Visual feedback when pressed | web, ios, android | 2.4.7 |
|
|
44
|
-
| `disabled_state` | Prevents interaction when disabled | web, ios, android | 4.1.2 |
|
|
45
44
|
| `loading_state` | Shows loading indicator | web, ios, android | 4.1.3 |
|
|
46
45
|
| `focus_ring` | WCAG 2.4.7 focus visible indicator | web, ios, android | 2.4.7 |
|
|
47
46
|
|
|
47
|
+
**Excluded**: `state_disabled` — DesignerPunk does not support disabled states for usability and accessibility reasons. If an action is unavailable, the component should not be rendered (adjudicated 2026-07-15).
|
|
48
|
+
|
|
48
49
|
---
|
|
49
50
|
|
|
50
51
|
## Usage
|
|
@@ -62,7 +63,6 @@ This component guarantees 7 behavioral contracts across all platforms:
|
|
|
62
63
|
variant="primary"
|
|
63
64
|
icon="arrow-right"
|
|
64
65
|
no-wrap="false"
|
|
65
|
-
disabled="false"
|
|
66
66
|
test-id="submit-button"
|
|
67
67
|
></button-cta>
|
|
68
68
|
```
|
|
@@ -122,7 +122,6 @@ ButtonCTA(
|
|
|
122
122
|
| `variant` | `'primary' \| 'secondary' \| 'tertiary'` | No | `'primary'` | Button visual variant |
|
|
123
123
|
| `icon` | `IconName` | No | - | Optional leading icon |
|
|
124
124
|
| `noWrap` | `boolean` | No | `false` | Prevent text wrapping |
|
|
125
|
-
| `disabled` | `boolean` | No | `false` | Disable button interaction |
|
|
126
125
|
| `onPress` | `() => void` | ✅ Yes | - | Press/click handler |
|
|
127
126
|
| `testID` | `string` | No | - | Test identifier |
|
|
128
127
|
|
|
@@ -177,7 +176,6 @@ ButtonCTA(
|
|
|
177
176
|
### Blend
|
|
178
177
|
- `blend.hoverDarker` - Hover state (8% darker)
|
|
179
178
|
- `blend.pressedDarker` - Pressed state (12% darker)
|
|
180
|
-
- `blend.disabledDesaturate` - Disabled state (12% less saturated)
|
|
181
179
|
- `blend.iconLighter` - Icon optical balance (8% lighter)
|
|
182
180
|
|
|
183
181
|
### Accessibility
|
|
@@ -205,7 +203,7 @@ ButtonCTA(
|
|
|
205
203
|
| 2.1.1 Keyboard | ✅ | Fully keyboard accessible (Tab, Enter, Space) |
|
|
206
204
|
| 2.4.7 Focus Visible | ✅ | Clear focus indicator with :focus-visible |
|
|
207
205
|
| 2.5.5 Target Size | ✅ | Touch targets meet 44px minimum |
|
|
208
|
-
| 4.1.2 Name, Role, Value | ✅ | Proper ARIA attributes
|
|
206
|
+
| 4.1.2 Name, Role, Value | ✅ | Proper ARIA attributes |
|
|
209
207
|
|
|
210
208
|
### Keyboard Navigation
|
|
211
209
|
|
|
@@ -219,7 +217,6 @@ ButtonCTA(
|
|
|
219
217
|
|
|
220
218
|
- Semantic `<button>` element provides implicit role
|
|
221
219
|
- `aria-label` set from label prop
|
|
222
|
-
- `aria-disabled="true"` when disabled
|
|
223
220
|
- State changes announced to assistive technology
|
|
224
221
|
|
|
225
222
|
---
|