diff --git a/2nd-gen/packages/core/components/meter/Meter.base.ts b/2nd-gen/packages/core/components/meter/Meter.base.ts new file mode 100644 index 00000000000..3d954b5b69b --- /dev/null +++ b/2nd-gen/packages/core/components/meter/Meter.base.ts @@ -0,0 +1,84 @@ +/** + * 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 { SpectrumElement } from '@spectrum-web-components/core/element/index.js'; +import { + LINEAR_PROGRESS_VALID_SIZES, + LinearProgressMixin, + SizedMixin, +} from '@spectrum-web-components/core/mixins/index.js'; + +import { METER_VARIANTS, type MeterVariant } from './Meter.types.js'; + +/** + * A non-focusable, read-only bar that shows a value inside a fixed range. + * Implements the WAI-ARIA `meter` role on the shadow `.swc-Meter` element + * (the host carries no ARIA). + * + * @attribute {ElementSize} size - The size of the meter. + */ +export abstract class MeterBase extends LinearProgressMixin( + SizedMixin(SpectrumElement, { + validSizes: LINEAR_PROGRESS_VALID_SIZES, + defaultSize: 'm', + }) +) { + /** + * The size of the meter. + * + * @default m + */ + declare public size: (typeof LINEAR_PROGRESS_VALID_SIZES)[number]; + + // ───────────────────────── + // API TO OVERRIDE + // ───────────────────────── + + /** + * @internal + * + * A readonly array of all valid variants for the meter. Concrete + * subclasses re-declare with their own valid set so validation logic + * resolves against `(this.constructor as typeof MeterBase).VARIANTS`. + */ + static readonly VARIANTS: readonly string[] = METER_VARIANTS; + + /** + * The variant of the meter. Drives the bar fill color. + */ + @property({ type: String, reflect: true }) + public variant: MeterVariant = 'informative'; + + // ────────────────────── + // IMPLEMENTATION + // ────────────────────── + + protected override update(changes: PropertyValues): void { + if (window.__swc?.DEBUG) { + const constructor = this.constructor as typeof MeterBase; + if (!constructor.VARIANTS.includes(this.variant)) { + window.__swc.warn( + this, + `<${this.localName}> element expects the "variant" attribute to be one of the following:`, + 'https://opensource.adobe.com/spectrum-web-components/components/meter/#variants', + { + issues: [...constructor.VARIANTS], + } + ); + } + } + super.update(changes); + } +} diff --git a/2nd-gen/packages/core/components/meter/Meter.types.ts b/2nd-gen/packages/core/components/meter/Meter.types.ts new file mode 100644 index 00000000000..4d47e8a7d51 --- /dev/null +++ b/2nd-gen/packages/core/components/meter/Meter.types.ts @@ -0,0 +1,19 @@ +/** + * 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 const METER_VARIANTS = [ + 'informative', + 'positive', + 'notice', + 'negative', +] as const; +export type MeterVariant = (typeof METER_VARIANTS)[number]; diff --git a/2nd-gen/packages/core/components/meter/index.ts b/2nd-gen/packages/core/components/meter/index.ts new file mode 100644 index 00000000000..c2366eb38b3 --- /dev/null +++ b/2nd-gen/packages/core/components/meter/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 './Meter.base.js'; +export * from './Meter.types.js'; diff --git a/2nd-gen/packages/core/mixins/index.ts b/2nd-gen/packages/core/mixins/index.ts index 321f1138dfb..cf1814d706d 100644 --- a/2nd-gen/packages/core/mixins/index.ts +++ b/2nd-gen/packages/core/mixins/index.ts @@ -11,6 +11,16 @@ */ export { DisabledMixin, type DisabledInterface } from './disabled-mixin.js'; +export { + LINEAR_PROGRESS_LABEL_POSITIONS, + LINEAR_PROGRESS_STATIC_COLORS, + LINEAR_PROGRESS_VALID_SIZES, + LinearProgressMixin, + type LinearProgressInterface, + type LinearProgressLabelPosition, + type LinearProgressSize, + type LinearProgressStaticColor, +} from './linear-progress-mixin.js'; export { ObserveSlotPresence, type SlotPresenceObservingInterface, diff --git a/2nd-gen/packages/core/mixins/linear-progress-mixin.ts b/2nd-gen/packages/core/mixins/linear-progress-mixin.ts new file mode 100644 index 00000000000..3fc1ee6c35e --- /dev/null +++ b/2nd-gen/packages/core/mixins/linear-progress-mixin.ts @@ -0,0 +1,276 @@ +/** + * 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, ReactiveElement } from 'lit'; +import { property } from 'lit/decorators.js'; + +import { + LanguageResolutionController, + languageResolverUpdatedSymbol, +} from '../controllers/language-resolution.js'; +import type { ElementSize } from './sized-mixin.js'; + +type Constructor> = { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + new (...args: any[]): T; + prototype: T; +}; + +export const LINEAR_PROGRESS_VALID_SIZES = [ + 's', + 'm', + 'l', + 'xl', +] as const satisfies readonly ElementSize[]; +export type LinearProgressSize = (typeof LINEAR_PROGRESS_VALID_SIZES)[number]; + +export const LINEAR_PROGRESS_STATIC_COLORS = ['white', 'black'] as const; +export type LinearProgressStaticColor = + (typeof LINEAR_PROGRESS_STATIC_COLORS)[number]; + +export const LINEAR_PROGRESS_LABEL_POSITIONS = ['top', 'side'] as const; +export type LinearProgressLabelPosition = + (typeof LINEAR_PROGRESS_LABEL_POSITIONS)[number]; + +const DEFAULT_FORMAT_OPTIONS: Intl.NumberFormatOptions = { style: 'percent' }; + +export interface LinearProgressInterface { + value: number; + minValue: number; + maxValue: number; + accessibleLabel: string; + valueLabel?: string; + formatOptions?: Intl.NumberFormatOptions; + labelPosition: LinearProgressLabelPosition; + staticColor?: LinearProgressStaticColor; + readonly clampedValue: number; + readonly fillPercent: number; + readonly formattedValue: string; + readonly hasLabelSlotContent: boolean; + readonly hasDescriptionSlotContent: boolean; + readonly labelContainerId: string; + readonly descriptionContainerId: string; + onLabelSlotChange(event: Event): void; + onDescriptionSlotChange(event: Event): void; +} + +let nextLinearProgressId = 0; + +function isSlotted(slot: HTMLSlotElement): boolean { + return slot.assignedNodes({ flatten: true }).some((node) => { + if (node.nodeType === Node.ELEMENT_NODE) { + return true; + } + if (node.nodeType === Node.TEXT_NODE) { + return Boolean(node.textContent?.trim()); + } + return false; + }); +} + +/** + * Thin mixin shared by linear progress components (``, future + * ``). Owns the typed property declarations, value + * clamping, locale formatting, slot tracking, and DEBUG accessible-name + * warning that both components need. + * + * Intentionally silent on `role` and animation — those stay in each + * component's own base class. + * + * Consumers wire the exposed `onLabelSlotChange` / `onDescriptionSlotChange` + * handlers to the `@slotchange` event on the corresponding slots in their + * render template, and read `clampedValue`, `fillPercent`, `formattedValue`, + * `labelContainerId`, `descriptionContainerId`, and the boolean slot flags + * from the same render template. + */ +export function LinearProgressMixin>( + constructor: T +): T & Constructor { + class LinearProgressElement + extends constructor + implements LinearProgressInterface + { + /** + * Current value. Clamped into `[minValue, maxValue]` before being + * exposed via `clampedValue` and `fillPercent`. + */ + @property({ type: Number, reflect: true }) + public value = 0; + + /** + * Bottom of the value range. + */ + @property({ type: Number, reflect: true, attribute: 'min-value' }) + public minValue = 0; + + /** + * Top of the value range. + */ + @property({ type: Number, reflect: true, attribute: 'max-value' }) + public maxValue = 100; + + /** + * Rare-case accessible-name fallback used when there is no visible + * `label` slot content (for example, a data grid of meters). + * Renders into `aria-label` on the role element when set. + */ + @property({ type: String, reflect: true, attribute: 'accessible-label' }) + public accessibleLabel = ''; + + /** + * Custom value text (e.g. `"1 of 4"`). Overrides the auto-formatted + * value in both rendered text and `aria-valuetext`. + */ + @property({ type: String, attribute: 'value-label' }) + public valueLabel?: string; + + /** + * `Intl.NumberFormatOptions` used to format the visible value and + * `aria-valuetext` when `valueLabel` is unset. JS property only — + * no attribute (full `Intl.NumberFormat` pass-through). + */ + @property({ attribute: false }) + public formatOptions?: Intl.NumberFormatOptions; + + /** + * Position of the label relative to the bar. + */ + @property({ type: String, reflect: true, attribute: 'label-position' }) + public labelPosition: LinearProgressLabelPosition = 'top'; + + /** + * Static color override for use on images or colored backgrounds. + */ + @property({ type: String, reflect: true, attribute: 'static-color' }) + public staticColor?: LinearProgressStaticColor; + + private readonly languageResolver = new LanguageResolutionController(this); + + private readonly _instanceId = ++nextLinearProgressId; + + private _hasLabelSlotContent = false; + private _hasDescriptionSlotContent = false; + + public get labelContainerId(): string { + return `swc-linear-progress-label-${this._instanceId}`; + } + + public get descriptionContainerId(): string { + return `swc-linear-progress-description-${this._instanceId}`; + } + + public get hasLabelSlotContent(): boolean { + return this._hasLabelSlotContent; + } + + public get hasDescriptionSlotContent(): boolean { + return this._hasDescriptionSlotContent; + } + + public get clampedValue(): number { + const value = Number.isFinite(this.value) ? this.value : 0; + const min = Number.isFinite(this.minValue) ? this.minValue : 0; + const max = Number.isFinite(this.maxValue) ? this.maxValue : 100; + const lo = Math.min(min, max); + const hi = Math.max(min, max); + return Math.min(hi, Math.max(lo, value)); + } + + public get fillPercent(): number { + const min = Number.isFinite(this.minValue) ? this.minValue : 0; + const max = Number.isFinite(this.maxValue) ? this.maxValue : 100; + if (max === min) { + return 0; + } + const fraction = (this.clampedValue - min) / (max - min); + return Math.min(100, Math.max(0, fraction * 100)); + } + + public get formattedValue(): string { + if (this.valueLabel) { + return this.valueLabel; + } + const min = Number.isFinite(this.minValue) ? this.minValue : 0; + const max = Number.isFinite(this.maxValue) ? this.maxValue : 100; + const options = this.formatOptions ?? DEFAULT_FORMAT_OPTIONS; + const formatter = new Intl.NumberFormat( + this.languageResolver.language, + options + ); + // Percent style consumes a fraction; every other style consumes the + // raw value. + if (options.style === 'percent') { + const fraction = + max === min ? 0 : (this.clampedValue - min) / (max - min); + return formatter.format(fraction); + } + return formatter.format(this.clampedValue); + } + + public onLabelSlotChange(event: Event): void { + const slot = event.target as HTMLSlotElement; + const next = isSlotted(slot); + if (next !== this._hasLabelSlotContent) { + this._hasLabelSlotContent = next; + this.requestUpdate(); + } + } + + public onDescriptionSlotChange(event: Event): void { + const slot = event.target as HTMLSlotElement; + const next = isSlotted(slot); + if (next !== this._hasDescriptionSlotContent) { + this._hasDescriptionSlotContent = next; + this.requestUpdate(); + } + } + + protected override willUpdate(changes: PropertyValues): void { + // Surfacing relevant property changes is enough — formatted output + // is derived from getters, so no internal state to recompute here. + // Hook is left for subclasses that need pre-render work. + super.willUpdate(changes); + } + + protected override updated(changes: PropertyValues): void { + super.updated(changes); + + if (changes.has(languageResolverUpdatedSymbol)) { + // Locale shifted; re-render so formatted value + aria-valuetext refresh. + this.requestUpdate(); + } + + if (window.__swc?.DEBUG) { + this.warnMissingAccessibleName(); + } + } + + private warnMissingAccessibleName(): void { + if (this._hasLabelSlotContent || this.accessibleLabel) { + return; + } + window.__swc?.warn( + this, + `<${this.localName}> requires an accessible name.`, + 'https://opensource.adobe.com/spectrum-web-components/components/meter/', + { + type: 'accessibility', + issues: [ + 'add visible label content via the "label" named slot, or', + 'set the "accessible-label" attribute (or "accessibleLabel" property) when there is no visible label, for example a data grid of meters.', + ], + } + ); + } + } + return LinearProgressElement as unknown as T & + Constructor; +} diff --git a/2nd-gen/packages/core/package.json b/2nd-gen/packages/core/package.json index 86b4009e996..dc61777086b 100644 --- a/2nd-gen/packages/core/package.json +++ b/2nd-gen/packages/core/package.json @@ -47,6 +47,10 @@ "types": "./dist/components/illustrated-message/index.d.ts", "import": "./dist/components/illustrated-message/index.js" }, + "./components/meter": { + "types": "./dist/components/meter/index.d.ts", + "import": "./dist/components/meter/index.js" + }, "./components/progress-circle": { "types": "./dist/components/progress-circle/index.d.ts", "import": "./dist/components/progress-circle/index.js" @@ -169,6 +173,9 @@ "components/illustrated-message": [ "dist/components/illustrated-message/index.d.ts" ], + "components/meter": [ + "dist/components/meter/index.d.ts" + ], "components/progress-circle": [ "dist/components/progress-circle/index.d.ts" ], diff --git a/2nd-gen/packages/swc/components/meter/Meter.ts b/2nd-gen/packages/swc/components/meter/Meter.ts new file mode 100644 index 00000000000..64b55f72df8 --- /dev/null +++ b/2nd-gen/packages/swc/components/meter/Meter.ts @@ -0,0 +1,136 @@ +/** + * 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 { property } from 'lit/decorators.js'; +import { classMap } from 'lit/directives/class-map.js'; +import { ifDefined } from 'lit/directives/if-defined.js'; + +import { + METER_VARIANTS, + MeterBase, + type MeterVariant, +} from '@spectrum-web-components/core/components/meter'; + +import styles from './meter.css'; + +/** + * A meter is a non-focusable, read-only bar that displays a value inside a + * fixed range. The WAI-ARIA `meter` role lives on the shadow `.swc-Meter` + * wrapper — the host carries no ARIA role. + * + * @element swc-meter + * @since 2.0.0 + * + * @example + * + * Storage used + * 2 GB of 10 GB used + * + * + * @slot label - Visible label for the meter. Referenced via `aria-labelledby` + * on the shadow `meter` role element. + * @slot description - Additional description text below the meter. Referenced + * via `aria-describedby` on the shadow `meter` role element + * when assigned nodes are present. + * + * @cssprop --swc-meter-fill-color - Bar fill color. Overrides the variant default. + * @cssprop --swc-meter-track-color - Bar track color. + * @cssprop --swc-meter-thickness - Bar thickness (block-size of the track and fill). + * @cssprop --swc-meter-min-width - Minimum inline-size of the meter. + * @cssprop --swc-meter-max-width - Maximum inline-size of the meter. + * @cssprop --swc-meter-description-spacing - Block-start spacing between the bar and the description slot. + * @cssprop --swc-meter-label-to-value-spacing - Inline spacing between label and value text in `label-position="side"`. + */ +export class Meter extends MeterBase { + // ──────────────────── + // API OVERRIDES + // ──────────────────── + + /** + * @internal + */ + static override readonly VARIANTS = METER_VARIANTS; + + // Re-declare so the `reflect: true` setting from the base is honored on + // the concrete element class (ES2022 class-field semantics). + @property({ type: String, reflect: true }) + public override variant: MeterVariant = 'informative'; + + // ────────────────────────────── + // RENDERING & STYLING + // ────────────────────────────── + + public static override get styles(): CSSResultArray { + return [styles]; + } + + protected override render(): TemplateResult { + const hasLabel = this.hasLabelSlotContent; + const hasDescription = this.hasDescriptionSlotContent; + const ariaLabelledBy = hasLabel ? this.labelContainerId : undefined; + const ariaLabel = + !hasLabel && this.accessibleLabel ? this.accessibleLabel : undefined; + const ariaDescribedBy = hasDescription + ? this.descriptionContainerId + : undefined; + + return html` + + + +
+ ${this.formattedValue} +
+
+
+
+ + + + `; + } +} diff --git a/2nd-gen/packages/swc/components/meter/index.ts b/2nd-gen/packages/swc/components/meter/index.ts new file mode 100644 index 00000000000..7dd00e5dd59 --- /dev/null +++ b/2nd-gen/packages/swc/components/meter/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 './Meter.js'; diff --git a/2nd-gen/packages/swc/components/meter/meter.css b/2nd-gen/packages/swc/components/meter/meter.css new file mode 100644 index 00000000000..6b6361525fe --- /dev/null +++ b/2nd-gen/packages/swc/components/meter/meter.css @@ -0,0 +1,192 @@ +/*! + * 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. + */ + +/* + * Meter styles. Shared bar/track/fill and label-layout rules are inlined + * here for the meter migration; they will be lifted into + * `swc/shared/linear-progress-base.css` when `` migrates. + * Tokens are sourced from spectrum-css spectrum-two + * `components/progressbar/index.css` and `components/meter/index.css`. + */ + +:host { + --swc-meter-thickness: token("progress-bar-thickness-medium"); + --swc-meter-corner-radius: token("corner-radius-full"); + --swc-meter-min-width: token("progress-bar-minimum-width"); + --swc-meter-max-width: token("progress-bar-maximum-width"); + --swc-meter-track-color: token("track-color"); + --swc-meter-fill-color: token("accent-content-color-default"); + --swc-meter-text-color: token("neutral-subdued-content-color-default"); + --swc-meter-font-size: token("font-size-100"); + --swc-meter-line-height: token("line-height-100"); + --swc-meter-line-height-cjk: token("cjk-line-height-100"); + --swc-meter-top-to-text: token("component-top-to-text-100"); + --swc-meter-label-to-track: token("spacing-75"); + --swc-meter-label-to-value-spacing: token("spacing-200"); + --swc-meter-description-spacing: token("spacing-75"); + + display: inline-block; + inline-size: 100%; + min-inline-size: var(--swc-meter-min-width); + max-inline-size: var(--swc-meter-max-width); + vertical-align: top; +} + +* { + box-sizing: border-box; +} + +.swc-Meter { + display: grid; + position: relative; + grid-template-areas: + "value" + "track"; + inline-size: 100%; +} + +.swc-Meter-label, +.swc-Meter-value { + margin-block-end: var(--swc-meter-label-to-track); + font-size: var(--swc-meter-font-size); + line-height: var(--swc-meter-line-height); + color: var(--swc-meter-text-color); + text-align: start; + overflow-wrap: break-word; + + &:lang(ja), + &:lang(zh), + &:lang(ko) { + line-height: var(--swc-meter-line-height-cjk); + } +} + +.swc-Meter-label { + margin-block-start: var(--swc-meter-top-to-text); +} + +.swc-Meter-label.is-empty { + display: none; +} + +.swc-Meter-value { + grid-area: value; + justify-self: end; + margin-inline-start: var(--swc-meter-label-to-value-spacing); + word-break: normal; +} + +.swc-Meter-track { + grid-area: track; + inline-size: 100%; + block-size: var(--swc-meter-thickness); + background: var(--swc-meter-track-color); + border-radius: var(--swc-meter-corner-radius); + overflow: hidden; +} + +.swc-Meter-fill { + block-size: var(--swc-meter-thickness); + background: var(--swc-meter-fill-color); + border: none; + border-radius: var(--swc-meter-corner-radius); + transition: inline-size 1s; +} + +.swc-Meter-description { + margin-block-start: var(--swc-meter-description-spacing); + font-size: var(--swc-meter-font-size); + line-height: var(--swc-meter-line-height); + color: var(--swc-meter-text-color); +} + +.swc-Meter-description.is-empty { + display: none; +} + +/* Variants */ +.swc-Meter--positive { + --swc-meter-fill-color: token("positive-visual-color"); +} + +.swc-Meter--notice { + --swc-meter-fill-color: token("notice-visual-color"); +} + +.swc-Meter--negative { + --swc-meter-fill-color: token("negative-visual-color"); +} + +/* Side label */ +.swc-Meter--sideLabel { + grid-template-areas: "label track value"; + grid-template-columns: auto 1fr auto; + align-items: center; +} + +.swc-Meter--sideLabel ~ .swc-Meter-label, +.swc-Meter--sideLabel .swc-Meter-label { + flex-grow: 0; + grid-area: label; + margin-block: 0; + margin-inline-end: var(--swc-meter-label-to-value-spacing); +} + +.swc-Meter--sideLabel .swc-Meter-value { + order: 3; + margin-block: 0; + text-align: end; +} + +/* Static colors */ +.swc-Meter--staticWhite { + --swc-meter-fill-color: token("static-white-track-indicator-color"); + --swc-meter-track-color: token("static-white-track-color"); + --swc-meter-text-color: token("static-white-text-color"); +} + +.swc-Meter--staticBlack { + --swc-meter-fill-color: token("static-black-track-indicator-color"); + --swc-meter-track-color: token("static-black-track-color"); + --swc-meter-text-color: token("static-black-text-color"); +} + +/* Sizes */ +:host([size="s"]) { + --swc-meter-thickness: token("progress-bar-thickness-small"); + --swc-meter-font-size: token("font-size-75"); + --swc-meter-top-to-text: token("component-top-to-text-75"); +} + +:host([size="l"]) { + --swc-meter-thickness: token("progress-bar-thickness-large"); + --swc-meter-font-size: token("font-size-200"); + --swc-meter-top-to-text: token("component-top-to-text-200"); +} + +:host([size="xl"]) { + --swc-meter-thickness: token("progress-bar-thickness-extra-large"); + --swc-meter-font-size: token("font-size-300"); + --swc-meter-top-to-text: token("component-top-to-text-300"); +} + +@media (forced-colors: active) { + .swc-Meter-track { + background: ButtonFace; + border: 1px solid ButtonText; + forced-color-adjust: none; + } + + .swc-Meter-fill { + background: ButtonText; + } +} diff --git a/2nd-gen/packages/swc/components/meter/swc-meter.ts b/2nd-gen/packages/swc/components/meter/swc-meter.ts new file mode 100644 index 00000000000..cdb3499354e --- /dev/null +++ b/2nd-gen/packages/swc/components/meter/swc-meter.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 { Meter } from './Meter.js'; + +declare global { + interface HTMLElementTagNameMap { + 'swc-meter': Meter; + } +} + +defineElement('swc-meter', Meter); diff --git a/2nd-gen/packages/swc/shared/linear-progress-base.css b/2nd-gen/packages/swc/shared/linear-progress-base.css new file mode 100644 index 00000000000..ceeb2c3867a --- /dev/null +++ b/2nd-gen/packages/swc/shared/linear-progress-base.css @@ -0,0 +1,18 @@ +/*! + * 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. + */ + +/* + * Shared bar/track/fill and label-layout rules for linear-progress + * components (``, future ``). Imported by + * each component's own stylesheet. Phase 2 stub — content lands in + * Phase 5 (Styling) per the meter migration plan. + */