From 3d7da5d4b5df6690df968ab3bc7a221e787c6f34 Mon Sep 17 00:00:00 2001 From: Tarun Tomar Date: Mon, 1 Jun 2026 14:25:07 +0530 Subject: [PATCH 01/14] chore(close-button): add structure for s2 migration Scaffold 2nd-gen close-button in core and swc: CloseButtonBase with static-color and sizing, minimal render and CSS, Storybook stories, and placeholder tests. Wire core package exports and mark Setup complete in the migration status table. Co-authored-by: Cursor --- .../close-button/CloseButton.base.ts | 107 ++++++++++++++++++ .../close-button/CloseButton.types.ts | 37 ++++++ .../core/components/close-button/index.ts | 13 +++ 2nd-gen/packages/core/package.json | 7 ++ .../components/close-button/CloseButton.ts | 90 +++++++++++++++ .../components/close-button/close-button.css | 50 ++++++++ .../swc/components/close-button/index.ts | 12 ++ .../stories/close-button.stories.ts | 88 ++++++++++++++ .../close-button/swc-close-button.ts | 22 ++++ .../test/close-button.a11y.spec.ts | 36 ++++++ .../close-button/test/close-button.test.ts | 30 +++++ .../01_status.md | 2 +- 12 files changed, 493 insertions(+), 1 deletion(-) create mode 100644 2nd-gen/packages/core/components/close-button/CloseButton.base.ts create mode 100644 2nd-gen/packages/core/components/close-button/CloseButton.types.ts create mode 100644 2nd-gen/packages/core/components/close-button/index.ts create mode 100644 2nd-gen/packages/swc/components/close-button/CloseButton.ts create mode 100644 2nd-gen/packages/swc/components/close-button/close-button.css create mode 100644 2nd-gen/packages/swc/components/close-button/index.ts create mode 100644 2nd-gen/packages/swc/components/close-button/stories/close-button.stories.ts create mode 100644 2nd-gen/packages/swc/components/close-button/swc-close-button.ts create mode 100644 2nd-gen/packages/swc/components/close-button/test/close-button.a11y.spec.ts create mode 100644 2nd-gen/packages/swc/components/close-button/test/close-button.test.ts diff --git a/2nd-gen/packages/core/components/close-button/CloseButton.base.ts b/2nd-gen/packages/core/components/close-button/CloseButton.base.ts new file mode 100644 index 00000000000..80554931522 --- /dev/null +++ b/2nd-gen/packages/core/components/close-button/CloseButton.base.ts @@ -0,0 +1,107 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { PropertyValues } from 'lit'; +import { property } from 'lit/decorators.js'; + +import { ButtonBase } from '@spectrum-web-components/core/components/button'; + +import { + CLOSE_BUTTON_VALID_SIZES, + type CloseButtonSize, + type CloseButtonStaticColor, + type CloseButtonVariant, +} from './CloseButton.types.js'; + +/** + * Abstract base for dismiss close-button semantics shared by rendering layers. + * Extends {@link ButtonBase} with close-button sizing and static-color API. + */ +export abstract class CloseButtonBase extends ButtonBase { + /** @internal */ + static override readonly VALID_SIZES: readonly CloseButtonSize[] = + CLOSE_BUTTON_VALID_SIZES; + + private _closeButtonSize: CloseButtonSize | null = null; + + /** + * Size of the close button. Defaults to medium when omitted; the host does + * not reflect `size` until authors set it explicitly (1st-gen parity). + */ + @property({ type: String }) + public override get size(): CloseButtonSize { + return this._closeButtonSize ?? 'm'; + } + + public override set size(value: CloseButtonSize) { + const normalized = ( + value ? (value as string).toLocaleLowerCase() : value + ) as CloseButtonSize; + const validSize: CloseButtonSize = CLOSE_BUTTON_VALID_SIZES.includes( + normalized + ) + ? normalized + : 'm'; + const oldSize = this._closeButtonSize ?? 'm'; + if (oldSize === validSize) { + return; + } + this._closeButtonSize = validSize; + if (value) { + this.setAttribute('size', validSize); + } else { + this.removeAttribute('size'); + } + this.requestUpdate('size', oldSize); + } + + /** + * @deprecated Use `static-color` instead. Deprecated alias mapping is + * completed in the API migration phase. + */ + @property({ reflect: true }) + public variant: CloseButtonVariant = ''; + + /** + * Static color treatment for display over colored or image backgrounds. + */ + @property({ type: String, reflect: true, attribute: 'static-color' }) + public staticColor?: CloseButtonStaticColor; + + /** + * Close buttons always render a cross icon; treat as icon-present for + * shared {@link ButtonBase} accessibility checks. + * + * @internal + */ + protected override get hasIcon(): boolean { + return true; + } + + protected override update(changedProperties: PropertyValues): void { + super.update(changedProperties); + if (this._closeButtonSize === null) { + this.removeAttribute('size'); + } + } + + protected override updated(changedProperties: PropertyValues): void { + if ( + changedProperties.has('variant') && + (this.variant === 'white' || this.variant === 'black') && + !this.staticColor + ) { + this.staticColor = this.variant; + } + super.updated(changedProperties); + } +} diff --git a/2nd-gen/packages/core/components/close-button/CloseButton.types.ts b/2nd-gen/packages/core/components/close-button/CloseButton.types.ts new file mode 100644 index 00000000000..33e1ba8c7a2 --- /dev/null +++ b/2nd-gen/packages/core/components/close-button/CloseButton.types.ts @@ -0,0 +1,37 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import type { ElementSize } from '@spectrum-web-components/core/mixins/index.js'; + +// ────────────────── +// SHARED +// ────────────────── + +export const CLOSE_BUTTON_VALID_SIZES = [ + 's', + 'm', + 'l', + 'xl', +] as const satisfies readonly ElementSize[]; + +export const CLOSE_BUTTON_STATIC_COLORS = ['white', 'black'] as const; + +export const CLOSE_BUTTON_VARIANTS = ['', 'white', 'black'] as const; + +// ────────────────── +// TYPES +// ────────────────── + +export type CloseButtonSize = (typeof CLOSE_BUTTON_VALID_SIZES)[number]; +export type CloseButtonStaticColor = + (typeof CLOSE_BUTTON_STATIC_COLORS)[number]; +export type CloseButtonVariant = (typeof CLOSE_BUTTON_VARIANTS)[number]; diff --git a/2nd-gen/packages/core/components/close-button/index.ts b/2nd-gen/packages/core/components/close-button/index.ts new file mode 100644 index 00000000000..f2970b0fae9 --- /dev/null +++ b/2nd-gen/packages/core/components/close-button/index.ts @@ -0,0 +1,13 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ +export * from './CloseButton.base.js'; +export * from './CloseButton.types.js'; diff --git a/2nd-gen/packages/core/package.json b/2nd-gen/packages/core/package.json index f5ce52ab92c..cededcdc960 100644 --- a/2nd-gen/packages/core/package.json +++ b/2nd-gen/packages/core/package.json @@ -43,6 +43,10 @@ "types": "./dist/components/button/index.d.ts", "import": "./dist/components/button/index.js" }, + "./components/close-button": { + "types": "./dist/components/close-button/index.d.ts", + "import": "./dist/components/close-button/index.js" + }, "./components/divider": { "types": "./dist/components/divider/index.d.ts", "import": "./dist/components/divider/index.js" @@ -198,6 +202,9 @@ "components/button": [ "dist/components/button/index.d.ts" ], + "components/close-button": [ + "dist/components/close-button/index.d.ts" + ], "components/divider": [ "dist/components/divider/index.d.ts" ], diff --git a/2nd-gen/packages/swc/components/close-button/CloseButton.ts b/2nd-gen/packages/swc/components/close-button/CloseButton.ts new file mode 100644 index 00000000000..3a1b0befa44 --- /dev/null +++ b/2nd-gen/packages/swc/components/close-button/CloseButton.ts @@ -0,0 +1,90 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { CSSResultArray, html, TemplateResult } from 'lit'; +import { classMap } from 'lit/directives/class-map.js'; +import { ifDefined } from 'lit/directives/if-defined.js'; + +import { + CloseButtonBase, + type CloseButtonSize, +} from '@spectrum-web-components/core/components/close-button'; + +import { Cross200Icon, Cross300Icon } from '../icon/elements/index.js'; + +import styles from './close-button.css'; + +const crossIconBySize: Record TemplateResult> = { + s: Cross200Icon, + m: Cross300Icon, + // @todo map to Cross400Icon when added to the 2nd-gen icon set. + l: Cross300Icon, + // @todo map to Cross500Icon when added to the 2nd-gen icon set. + xl: Cross300Icon, +}; + +/** + * A compact dismiss control for dialogs, banners, toasts, and similar chrome. + * + * @element swc-close-button + * @since 0.0.1 + * + * @slot - Accessible text label rendered visually hidden next to the cross icon. + * + * @example + * + * + * @example + * Close + */ +export class CloseButton extends CloseButtonBase { + // ────────────────────────────── + // RENDERING & STYLING + // ────────────────────────────── + + public static override get styles(): CSSResultArray { + return [styles]; + } + + protected override render(): TemplateResult { + const resolvedName = this.getResolvedAccessibleName(); + + return html` + + `; + } +} diff --git a/2nd-gen/packages/swc/components/close-button/close-button.css b/2nd-gen/packages/swc/components/close-button/close-button.css new file mode 100644 index 00000000000..f85ebf2d135 --- /dev/null +++ b/2nd-gen/packages/swc/components/close-button/close-button.css @@ -0,0 +1,50 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +:host { + display: inline-flex; +} + +.swc-CloseButton { + display: inline-flex; + align-items: center; + justify-content: center; + padding: 0; + background: transparent; + border: none; + cursor: pointer; +} + +.swc-CloseButton:disabled { + cursor: default; +} + +.swc-CloseButton-icon { + display: block; + flex-shrink: 0; +} + +.swc-CloseButton-icon svg { + display: block; +} + +.swc-CloseButton-label { + position: absolute; + inline-size: 1px; + block-size: 1px; + padding: 0; + margin: -1px; + white-space: nowrap; + border: 0; + overflow: hidden; + clip-path: inset(50%); +} diff --git a/2nd-gen/packages/swc/components/close-button/index.ts b/2nd-gen/packages/swc/components/close-button/index.ts new file mode 100644 index 00000000000..fc250839695 --- /dev/null +++ b/2nd-gen/packages/swc/components/close-button/index.ts @@ -0,0 +1,12 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ +export * from './CloseButton.js'; diff --git a/2nd-gen/packages/swc/components/close-button/stories/close-button.stories.ts b/2nd-gen/packages/swc/components/close-button/stories/close-button.stories.ts new file mode 100644 index 00000000000..9e833b8d212 --- /dev/null +++ b/2nd-gen/packages/swc/components/close-button/stories/close-button.stories.ts @@ -0,0 +1,88 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import type { Meta, StoryObj as Story } from '@storybook/web-components'; +import { getStorybookHelpers } from '@wc-toolkit/storybook-helpers'; + +import { + CLOSE_BUTTON_STATIC_COLORS, + CLOSE_BUTTON_VALID_SIZES, +} from '@spectrum-web-components/core/components/close-button'; + +import '@adobe/spectrum-wc/components/close-button/swc-close-button.js'; + +// ──────────────── +// METADATA +// ──────────────── + +const { args, argTypes, template } = getStorybookHelpers('swc-close-button'); + +argTypes.size = { + ...argTypes.size, + control: { type: 'select' }, + options: CLOSE_BUTTON_VALID_SIZES, +}; + +argTypes['static-color'] = { + ...argTypes['static-color'], + control: 'select', + options: ['', ...CLOSE_BUTTON_STATIC_COLORS], + table: { + ...argTypes['static-color']?.table, + category: 'attributes', + }, +}; + +args['accessible-label'] = 'Close'; + +/** + * Close buttons dismiss dialogs, banners, toasts, and similar surfaces. They + * are compact, icon-forward controls that require an accessible name describing + * the dismiss action. + */ +const meta: Meta = { + title: 'Close Button', + component: 'swc-close-button', + args, + argTypes, + render: (args) => template(args), + parameters: { + docs: { + subtitle: 'Compact dismiss control for overlays and chrome regions', + }, + }, + tags: ['migrated'], +}; + +export default meta; + +// ──────────────────── +// AUTODOCS STORY +// ──────────────────── + +export const Playground: Story = { + args: { + 'accessible-label': 'Close', + }, + tags: ['autodocs', 'dev'], +}; + +// ────────────────────────── +// OVERVIEW STORY +// ────────────────────────── + +export const Overview: Story = { + args: { + 'accessible-label': 'Close', + }, + tags: ['overview'], +}; diff --git a/2nd-gen/packages/swc/components/close-button/swc-close-button.ts b/2nd-gen/packages/swc/components/close-button/swc-close-button.ts new file mode 100644 index 00000000000..43239f19769 --- /dev/null +++ b/2nd-gen/packages/swc/components/close-button/swc-close-button.ts @@ -0,0 +1,22 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ +import { defineElement } from '@spectrum-web-components/core/element/index.js'; + +import { CloseButton } from './CloseButton.js'; + +declare global { + interface HTMLElementTagNameMap { + 'swc-close-button': CloseButton; + } +} + +defineElement('swc-close-button', CloseButton); diff --git a/2nd-gen/packages/swc/components/close-button/test/close-button.a11y.spec.ts b/2nd-gen/packages/swc/components/close-button/test/close-button.a11y.spec.ts new file mode 100644 index 00000000000..ce808728ff0 --- /dev/null +++ b/2nd-gen/packages/swc/components/close-button/test/close-button.a11y.spec.ts @@ -0,0 +1,36 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { expect, test } from '@playwright/test'; + +import { gotoStory } from '../../../utils/a11y-helpers.js'; + +/** + * Accessibility tests for Close Button component (2nd Generation). + * + * Placeholder: ARIA snapshot coverage is added in Phase 6. + */ + +test.describe('Close Button - ARIA Snapshots', () => { + test('should expose a named dismiss button in the overview story', async ({ + page, + }) => { + const root = await gotoStory( + page, + 'components-close-button--overview', + 'swc-close-button' + ); + await expect(root).toMatchAriaSnapshot(` + - button "Close" + `); + }); +}); diff --git a/2nd-gen/packages/swc/components/close-button/test/close-button.test.ts b/2nd-gen/packages/swc/components/close-button/test/close-button.test.ts new file mode 100644 index 00000000000..6842a1cc767 --- /dev/null +++ b/2nd-gen/packages/swc/components/close-button/test/close-button.test.ts @@ -0,0 +1,30 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import type { Meta, StoryObj as Story } from '@storybook/web-components'; + +import meta, { Overview } from '../stories/close-button.stories.js'; + +export default { + ...meta, + title: 'Close Button/Tests', + parameters: { + ...meta.parameters, + docs: { disable: true, page: null }, + }, + tags: ['!autodocs', 'dev'], +} as Meta; + +// Placeholder: play function with component-specific assertions is added in Phase 6. +export const OverviewTest: Story = { + ...Overview, +}; diff --git a/CONTRIBUTOR-DOCS/03_project-planning/02_workstreams/02_2nd-gen-component-migration/01_status.md b/CONTRIBUTOR-DOCS/03_project-planning/02_workstreams/02_2nd-gen-component-migration/01_status.md index 3303eb4a3a1..8d068257bfd 100644 --- a/CONTRIBUTOR-DOCS/03_project-planning/02_workstreams/02_2nd-gen-component-migration/01_status.md +++ b/CONTRIBUTOR-DOCS/03_project-planning/02_workstreams/02_2nd-gen-component-migration/01_status.md @@ -36,7 +36,7 @@ | Card | | | | | | | | | Checkbox | ✓ | | | | | | | | Clear Button | | | | | | | | -| Close Button | | | | | | | | +| Close Button | ✓ | ✓ | | | | | | | Coachmark | | | | | | | | | Color Area | | | | | | | | | Color Field | ✓ | | | | | | | From b896859f94f7fb6e528489dcfdf45579abb87f7a Mon Sep 17 00:00:00 2001 From: Tarun Tomar Date: Mon, 1 Jun 2026 15:05:21 +0530 Subject: [PATCH 02/14] chore(close-button): add Cross400 and Cross500 icons for l and xl sizes Wire close-button l/xl to spectrum-css UI icon paths instead of reusing Cross300. Co-authored-by: Cursor --- .../components/close-button/CloseButton.ts | 13 ++++++----- .../components/icon/elements/Cross400Icon.ts | 22 +++++++++++++++++++ .../components/icon/elements/Cross500Icon.ts | 22 +++++++++++++++++++ .../swc/components/icon/elements/index.ts | 2 ++ 4 files changed, 54 insertions(+), 5 deletions(-) create mode 100644 2nd-gen/packages/swc/components/icon/elements/Cross400Icon.ts create mode 100644 2nd-gen/packages/swc/components/icon/elements/Cross500Icon.ts diff --git a/2nd-gen/packages/swc/components/close-button/CloseButton.ts b/2nd-gen/packages/swc/components/close-button/CloseButton.ts index 3a1b0befa44..796d5e36d09 100644 --- a/2nd-gen/packages/swc/components/close-button/CloseButton.ts +++ b/2nd-gen/packages/swc/components/close-button/CloseButton.ts @@ -19,17 +19,20 @@ import { type CloseButtonSize, } from '@spectrum-web-components/core/components/close-button'; -import { Cross200Icon, Cross300Icon } from '../icon/elements/index.js'; +import { + Cross200Icon, + Cross300Icon, + Cross400Icon, + Cross500Icon, +} from '../icon/elements/index.js'; import styles from './close-button.css'; const crossIconBySize: Record TemplateResult> = { s: Cross200Icon, m: Cross300Icon, - // @todo map to Cross400Icon when added to the 2nd-gen icon set. - l: Cross300Icon, - // @todo map to Cross500Icon when added to the 2nd-gen icon set. - xl: Cross300Icon, + l: Cross400Icon, + xl: Cross500Icon, }; /** diff --git a/2nd-gen/packages/swc/components/icon/elements/Cross400Icon.ts b/2nd-gen/packages/swc/components/icon/elements/Cross400Icon.ts new file mode 100644 index 00000000000..16416d159a3 --- /dev/null +++ b/2nd-gen/packages/swc/components/icon/elements/Cross400Icon.ts @@ -0,0 +1,22 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ +import { html, TemplateResult } from 'lit'; + +export const Cross400Icon = (): TemplateResult => { + return html` + + + + `; +}; diff --git a/2nd-gen/packages/swc/components/icon/elements/Cross500Icon.ts b/2nd-gen/packages/swc/components/icon/elements/Cross500Icon.ts new file mode 100644 index 00000000000..a033816c826 --- /dev/null +++ b/2nd-gen/packages/swc/components/icon/elements/Cross500Icon.ts @@ -0,0 +1,22 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ +import { html, TemplateResult } from 'lit'; + +export const Cross500Icon = (): TemplateResult => { + return html` + + + + `; +}; diff --git a/2nd-gen/packages/swc/components/icon/elements/index.ts b/2nd-gen/packages/swc/components/icon/elements/index.ts index b30f66ddd2c..b681852df39 100644 --- a/2nd-gen/packages/swc/components/icon/elements/index.ts +++ b/2nd-gen/packages/swc/components/icon/elements/index.ts @@ -25,6 +25,8 @@ export * from './Cross75Icon.js'; export * from './Cross100Icon.js'; export * from './Cross200Icon.js'; export * from './Cross300Icon.js'; +export * from './Cross400Icon.js'; +export * from './Cross500Icon.js'; export * from './Dash75Icon.js'; export * from './Dash100Icon.js'; export * from './Dash200Icon.js'; From 300b3f91ac99267ac1f71d1bc545134df778fcc9 Mon Sep 17 00:00:00 2001 From: Tarun Tomar Date: Tue, 2 Jun 2026 13:28:30 +0530 Subject: [PATCH 03/14] docs(close-button): mark Phase 2 setup complete after scaffold Rebase onto main with planning docs merged; tick Setup checklist and status table. Co-authored-by: Cursor --- .../02_2nd-gen-component-migration/01_status.md | 2 +- .../03_components/close-button/migration-plan.md | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTOR-DOCS/03_project-planning/02_workstreams/02_2nd-gen-component-migration/01_status.md b/CONTRIBUTOR-DOCS/03_project-planning/02_workstreams/02_2nd-gen-component-migration/01_status.md index 8d068257bfd..05856bbb9f6 100644 --- a/CONTRIBUTOR-DOCS/03_project-planning/02_workstreams/02_2nd-gen-component-migration/01_status.md +++ b/CONTRIBUTOR-DOCS/03_project-planning/02_workstreams/02_2nd-gen-component-migration/01_status.md @@ -36,7 +36,7 @@ | Card | | | | | | | | | Checkbox | ✓ | | | | | | | | Clear Button | | | | | | | | -| Close Button | ✓ | ✓ | | | | | | +| Close Button | ✓ | ✓ | ✓ | | ✓ | | | | Coachmark | | | | | | | | | Color Area | | | | | | | | | Color Field | ✓ | | | | | | | diff --git a/CONTRIBUTOR-DOCS/03_project-planning/03_components/close-button/migration-plan.md b/CONTRIBUTOR-DOCS/03_project-planning/03_components/close-button/migration-plan.md index edae099060f..5035b9620c5 100644 --- a/CONTRIBUTOR-DOCS/03_project-planning/03_components/close-button/migration-plan.md +++ b/CONTRIBUTOR-DOCS/03_project-planning/03_components/close-button/migration-plan.md @@ -215,9 +215,9 @@ Prerequisite dependency: - [x] Migration plan exists and is reviewable. ### Setup -- [ ] Create `2nd-gen/packages/core/components/close-button/`. -- [ ] Create `2nd-gen/packages/swc/components/close-button/`. -- [ ] Wire exports and package entrypoints. +- [x] Create `2nd-gen/packages/core/components/close-button/`. +- [x] Create `2nd-gen/packages/swc/components/close-button/`. +- [x] Wire exports and package entrypoints. ### API - [ ] Define `size`, `static-color`, `accessible-label`, `disabled`. From 91eaa6336630d2b3b4145ca1c33fab0366b33ca4 Mon Sep 17 00:00:00 2001 From: Tarun Tomar Date: Tue, 2 Jun 2026 15:31:41 +0530 Subject: [PATCH 04/14] fix(close-button): give scaffold CSS dimensions so a11y tests see a visible control Add size and icon tokens so Playwright can wait for swc-close-button in overview stories. Co-authored-by: Cursor --- .../components/close-button/close-button.css | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/2nd-gen/packages/swc/components/close-button/close-button.css b/2nd-gen/packages/swc/components/close-button/close-button.css index f85ebf2d135..cd2d72c1f5f 100644 --- a/2nd-gen/packages/swc/components/close-button/close-button.css +++ b/2nd-gen/packages/swc/components/close-button/close-button.css @@ -15,10 +15,16 @@ } .swc-CloseButton { + --_swc-close-button-block-size: token("component-height-100"); + --_swc-close-button-icon-size: token("cross-icon-size-300"); + display: inline-flex; align-items: center; justify-content: center; + inline-size: var(--_swc-close-button-block-size); + block-size: var(--_swc-close-button-block-size); padding: 0; + color: token("neutral-content-color-default"); background: transparent; border: none; cursor: pointer; @@ -28,13 +34,36 @@ cursor: default; } +:host([size="s"]) .swc-CloseButton { + --_swc-close-button-block-size: token("component-height-75"); + --_swc-close-button-icon-size: token("cross-icon-size-200"); +} + +:host([size="l"]) .swc-CloseButton { + --_swc-close-button-block-size: token("component-height-200"); + --_swc-close-button-icon-size: token("cross-icon-size-400"); +} + +:host([size="xl"]) .swc-CloseButton { + --_swc-close-button-block-size: token("component-height-300"); + --_swc-close-button-icon-size: token("cross-icon-size-500"); +} + .swc-CloseButton-icon { display: block; flex-shrink: 0; + inline-size: var(--_swc-close-button-icon-size); + block-size: var(--_swc-close-button-icon-size); } .swc-CloseButton-icon svg { display: block; + inline-size: 100%; + block-size: 100%; +} + +.swc-CloseButton-icon svg path { + fill: currentcolor; } .swc-CloseButton-label { From b7d4ec21cdd8493cff6c5e2177b4491200646c04 Mon Sep 17 00:00:00 2001 From: Tarun Tomar Date: Tue, 2 Jun 2026 16:00:07 +0530 Subject: [PATCH 05/14] fix(close-button): inherit SizedMixin size reflection from ButtonBase Co-authored-by: Cursor --- .../close-button/CloseButton.base.ts | 47 +++---------------- 1 file changed, 7 insertions(+), 40 deletions(-) diff --git a/2nd-gen/packages/core/components/close-button/CloseButton.base.ts b/2nd-gen/packages/core/components/close-button/CloseButton.base.ts index 80554931522..b8bab26aabb 100644 --- a/2nd-gen/packages/core/components/close-button/CloseButton.base.ts +++ b/2nd-gen/packages/core/components/close-button/CloseButton.base.ts @@ -25,45 +25,19 @@ import { /** * Abstract base for dismiss close-button semantics shared by rendering layers. * Extends {@link ButtonBase} with close-button sizing and static-color API. + * + * @slot - Accessible text label rendered visually hidden next to the cross icon. + * + * @attribute {CloseButtonSize} size - Visual size of the close button. + * @attribute {'white' | 'black'} static-color - Static color treatment for display over colored or image backgrounds. + * @attribute {boolean} disabled - Whether the button is disabled. + * @attribute {'white' | 'black'} variant - Deprecated alias for `static-color`. */ export abstract class CloseButtonBase extends ButtonBase { /** @internal */ static override readonly VALID_SIZES: readonly CloseButtonSize[] = CLOSE_BUTTON_VALID_SIZES; - private _closeButtonSize: CloseButtonSize | null = null; - - /** - * Size of the close button. Defaults to medium when omitted; the host does - * not reflect `size` until authors set it explicitly (1st-gen parity). - */ - @property({ type: String }) - public override get size(): CloseButtonSize { - return this._closeButtonSize ?? 'm'; - } - - public override set size(value: CloseButtonSize) { - const normalized = ( - value ? (value as string).toLocaleLowerCase() : value - ) as CloseButtonSize; - const validSize: CloseButtonSize = CLOSE_BUTTON_VALID_SIZES.includes( - normalized - ) - ? normalized - : 'm'; - const oldSize = this._closeButtonSize ?? 'm'; - if (oldSize === validSize) { - return; - } - this._closeButtonSize = validSize; - if (value) { - this.setAttribute('size', validSize); - } else { - this.removeAttribute('size'); - } - this.requestUpdate('size', oldSize); - } - /** * @deprecated Use `static-color` instead. Deprecated alias mapping is * completed in the API migration phase. @@ -87,13 +61,6 @@ export abstract class CloseButtonBase extends ButtonBase { return true; } - protected override update(changedProperties: PropertyValues): void { - super.update(changedProperties); - if (this._closeButtonSize === null) { - this.removeAttribute('size'); - } - } - protected override updated(changedProperties: PropertyValues): void { if ( changedProperties.has('variant') && From c87e8c60b8dd513b7ad193a5200865bdeb1b1e0d Mon Sep 17 00:00:00 2001 From: Tarun Tomar Date: Tue, 2 Jun 2026 16:19:33 +0530 Subject: [PATCH 06/14] fix(close-button): omit pending from close-button public surface Drop pending wiring from the SWC template, mark inherited pending APIs @internal on CloseButton, hide them in Storybook, and document the scope in the migration plan. Co-authored-by: Cursor --- .../components/close-button/CloseButton.ts | 23 +++++++++++-------- .../stories/close-button.stories.ts | 8 +++++++ .../close-button/migration-plan.md | 3 ++- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/2nd-gen/packages/swc/components/close-button/CloseButton.ts b/2nd-gen/packages/swc/components/close-button/CloseButton.ts index 796d5e36d09..585721b0b45 100644 --- a/2nd-gen/packages/swc/components/close-button/CloseButton.ts +++ b/2nd-gen/packages/swc/components/close-button/CloseButton.ts @@ -11,6 +11,7 @@ */ import { CSSResultArray, html, TemplateResult } from 'lit'; +import { property } from 'lit/decorators.js'; import { classMap } from 'lit/directives/class-map.js'; import { ifDefined } from 'lit/directives/if-defined.js'; @@ -50,6 +51,18 @@ const crossIconBySize: Record TemplateResult> = { * Close */ export class CloseButton extends CloseButtonBase { + /** + * @internal + */ + @property({ type: Boolean, reflect: true }) + public override pending = false; + + /** + * @internal + */ + @property({ type: String, attribute: 'pending-label' }) + public override pendingLabel?: string; + // ────────────────────────────── // RENDERING & STYLING // ────────────────────────────── @@ -67,19 +80,11 @@ export class CloseButton extends CloseButtonBase { 'swc-CloseButton': true, 'swc-CloseButton--staticWhite': this.staticColor === 'white', 'swc-CloseButton--staticBlack': this.staticColor === 'black', - 'swc-CloseButton--pendingActive': this.pendingActive, })} type="button" @click=${this.handleClick} ?disabled=${this.disabled} - aria-disabled=${ifDefined( - this.pending && !this.disabled ? 'true' : undefined - )} - aria-label=${ifDefined( - this.pending - ? this.getPendingAccessibleName() - : (resolvedName ?? undefined) - )} + aria-label=${ifDefined(resolvedName ?? undefined)} >