From 343d02c40d96205789be270b8056b2fdac3df9cc Mon Sep 17 00:00:00 2001 From: Juergen Kunz Date: Wed, 11 Mar 2026 19:35:16 +0000 Subject: [PATCH] fix(decorators): patch container-responsive styles to fix stale container queries and add customElement wrapper that sets .is and warns on class/tag mismatch --- changelog.md | 8 ++++++ ts/00_commitinfo_data.ts | 2 +- ts/decorators.containerresponsive.ts | 40 +++++++++++++++++++++++----- ts/decorators.customelement.ts | 25 +++++++++++++++++ ts/index.ts | 2 +- 5 files changed, 69 insertions(+), 8 deletions(-) create mode 100644 ts/decorators.customelement.ts diff --git a/changelog.md b/changelog.md index 7d3797c..8c86406 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,13 @@ # Changelog +## 2026-03-11 - 2.2.2 - fix(decorators) +patch container-responsive styles to fix stale container queries and add customElement wrapper that sets .is and warns on class/tag mismatch + +- containerResponsive now derives the kebab name and replaces stale '@container ' occurrences inside CSSResult cssText using unsafeCSS to correct container queries produced before decorators ran +- containerResponsive appends containerContextStyles to the component styles while preserving/processing existing static styles +- Added a customElement decorator wrapper that assigns .is on the class, logs a warning if the class name kebab-cases to a different tag name, and delegates to Lit's customElement +- ts/index.ts now re-exports customElement from the local decorator so the new behavior is used project-wide + ## 2026-03-11 - 2.2.1 - fix(dees-element) rename cssForCustom to cssForConstraint, remove DeesElement static cssFor* helpers, and add optional elementClass parameter to cssManager breakpoint helpers diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index a9381c4..bb9fe3d 100644 --- a/ts/00_commitinfo_data.ts +++ b/ts/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@design.estate/dees-element', - version: '2.2.1', + version: '2.2.2', description: 'A library for creating custom elements extending the lit element class with additional functionalities.' } diff --git a/ts/decorators.containerresponsive.ts b/ts/decorators.containerresponsive.ts index 1627163..971a730 100644 --- a/ts/decorators.containerresponsive.ts +++ b/ts/decorators.containerresponsive.ts @@ -1,19 +1,47 @@ +import { unsafeCSS, type CSSResult } from 'lit'; import * as domtools from '@design.estate/dees-domtools'; +const camelToKebab = (name: string) => + name.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); + export function containerResponsive() { return function (target: any) { const tagName: string = target.is || target.name.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); - const containerStyles = domtools.breakpoints.containerContextStyles(tagName); - const original = target.styles; - if (Array.isArray(original)) { - target.styles = [...original, containerStyles]; - } else if (original) { - target.styles = [original, containerStyles]; + // If .is differs from the regex-derived name, cssManager.cssFor*(css, this) + // in static styles (evaluated before decorators) used the wrong container name. + // Fix those stale references now. + const derivedName = target.name ? camelToKebab(target.name) : null; + if (derivedName && derivedName !== tagName) { + const fixStyle = (style: CSSResult) => { + if (style && style.cssText && style.cssText.includes(`@container ${derivedName}`)) { + return unsafeCSS( + style.cssText.replaceAll(`@container ${derivedName}`, `@container ${tagName}`) + ); + } + return style; + }; + + const original = target.styles; + if (Array.isArray(original)) { + target.styles = original.map(fixStyle); + } else if (original) { + target.styles = fixStyle(original); + } + } + + // Append containment context styles + const containerStyles = domtools.breakpoints.containerContextStyles(tagName); + const current = target.styles; + if (Array.isArray(current)) { + target.styles = [...current, containerStyles]; + } else if (current) { + target.styles = [current, containerStyles]; } else { target.styles = [containerStyles]; } + return target; }; } diff --git a/ts/decorators.customelement.ts b/ts/decorators.customelement.ts new file mode 100644 index 0000000..e76baec --- /dev/null +++ b/ts/decorators.customelement.ts @@ -0,0 +1,25 @@ +import { customElement as litCustomElement } from 'lit/decorators/custom-element.js'; + +const camelToKebab = (name: string) => + name.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); + +export function customElement(tagName: string) { + return (classOrTarget: any, context?: any) => { + // Set .is so that other decorators and utilities can read the tag name + classOrTarget.is = tagName; + + // Warn if class name convention doesn't match the tag + if (classOrTarget.name) { + const derived = camelToKebab(classOrTarget.name); + if (derived !== tagName) { + console.warn( + `[dees-element] Class "${classOrTarget.name}" kebab-cases to "${derived}" but tag is "${tagName}". ` + + `Container queries use .is ("${tagName}").` + ); + } + } + + // Delegate to Lit's original decorator + return litCustomElement(tagName)(classOrTarget, context); + }; +} diff --git a/ts/index.ts b/ts/index.ts index f2d47f0..73e3aed 100644 --- a/ts/index.ts +++ b/ts/index.ts @@ -7,7 +7,7 @@ export { html as static, unsafeStatic } from 'lit/static-html.js'; export { unsafeHTML } from 'lit/directives/unsafe-html.js'; -export { customElement } from 'lit/decorators/custom-element.js'; +export { customElement } from './decorators.customelement.js'; export { property, state, query, queryAll, queryAsync } from 'lit/decorators.js';