From 2dfc23c186114edb18412f7ee9de6b87b050e0c7 Mon Sep 17 00:00:00 2001 From: Foscat Date: Mon, 20 Jul 2026 16:51:10 -0500 Subject: [PATCH 01/13] feat: align layout ecosystem fixtures for 2.1 --- .github/workflows/npm-publish.yml | 2 +- CHANGELOG.md | 12 ++ README.md | 31 +++-- demo/demo.js | 171 ++++++++++++++++++------ demo/index.html | 38 +++--- demo/site.webmanifest | 2 +- docs/wiki/Demo-And-GitHub-Pages.md | 2 +- docs/wiki/Getting-Started.md | 2 +- docs/wiki/Home.md | 6 +- docs/wiki/Installation-And-CDN.md | 17 ++- docs/wiki/Migrating-To-2.0.md | 14 +- docs/wiki/Release-And-Publishing.md | 14 +- docs/wiki/UI-Style-Kit-Compatibility.md | 17 ++- package-lock.json | 39 +++--- package.json | 6 +- test/demo-smoke.test.mjs | 151 +++++++++++++-------- test/layout-css-contract.test.mjs | 79 +++++++---- test/pages-artifact.test.mjs | 8 +- test/release-docs-contract.test.mjs | 17 +-- 19 files changed, 400 insertions(+), 228 deletions(-) diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index 82751c8..8bc0b3a 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -6,7 +6,7 @@ on: workflow_dispatch: inputs: release_tag: - description: "Release tag to publish, for example v2.0.0" + description: "Release tag to publish, for example v2.1.0" required: true type: string diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f46ba6..e813964 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,18 @@ All notable changes to `layout-style-css` are documented here. This project follows semantic versioning. +## [2.1.0] - 2026-07-20 + +### Changed + +- Updated the ecosystem demo and documentation to use `ui-style-kit-css@2.1.0` visual CSS, `ui-style-kit-css/interactive-surface-theme.css`, `interactive-surface-css@1.5.0` state core, and Layout core as the canonical import order. +- Kept `layout-style-css/integrations/ui-style-kit.css` available as a frozen deprecated structural bridge for legacy UI-prefixed layout aliases. +- Updated release metadata and the GitHub Pages demo to identify the 2.1.0 package line. + +### Tests + +- Switched demo fixtures to consume the staged UI Style Kit 2.1 manifest, visual bundle, and token bridge while locking Interactive Surface to the released 1.5.0 package. + ## [2.0.0] - 2026-07-19 ### Breaking diff --git a/README.md b/README.md index 8e605f5..3752e37 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Container-first, dependency-free structural CSS for responsive application layouts. -`layout-style-css@2.0.0` provides semantic wrappers, composition primitives, seven functional recipes, ordering escape hatches, and sixteen spatial personalities. It owns layout only; color, typography, borders, shadows, component paint, and interactive states belong to other libraries. +`layout-style-css@2.1.0` provides semantic wrappers, composition primitives, seven functional recipes, ordering escape hatches, sixteen spatial personalities, and refreshed ecosystem fixtures for UI Style Kit CSS 2.1 and Interactive Surface CSS 1.5. It owns layout only; color, typography, borders, shadows, component paint, and interactive states belong to other libraries. ## Requirements @@ -10,12 +10,12 @@ Container-first, dependency-free structural CSS for responsive application layou - Current evergreen Chromium, Firefox, or WebKit for consumers - No runtime or peer dependencies -`ui-style-kit-css@2.0.1` and `interactive-surface-css@1.4.0` are exact development and integration fixtures. They are not installed for consumers. A separate UI Style Kit revision is a follow-up and is not part of this release. +`ui-style-kit-css@2.1.0` and `interactive-surface-css@1.5.0` are development and integration fixtures. UI Style Kit is resolved from the sibling staged checkout until 2.1.0 is approved and published; Interactive Surface 1.5.0 is the released registry fixture. Neither package is installed for consumers. ## Install ```bash -npm install layout-style-css@2.0.0 +npm install layout-style-css@2.1.0 ``` Standalone use needs one import: @@ -33,21 +33,20 @@ Layout Style never imports companion packages. Install and import each layer exp Layout plus UI Style Kit: ```js -import "ui-style-kit-css/with-bridge.css"; -import "layout-style-css/integrations/ui-style-kit.css"; +import "ui-style-kit-css/visual.css"; import "layout-style-css"; ``` Layout plus UI Style Kit and Interactive Surface must use this order: ```js -import "ui-style-kit-css/with-bridge.css"; +import "ui-style-kit-css/visual.css"; +import "ui-style-kit-css/interactive-surface-theme.css"; import "interactive-surface-css/state-core.css"; -import "layout-style-css/integrations/ui-style-kit.css"; import "layout-style-css"; ``` -The UI Style Kit bridge establishes its visual tokens, Interactive Surface contributes interaction states, the Layout integration maps structural UI aliases, and Layout core applies spatial behavior. +UI Style Kit establishes visual paint, the UI token bridge maps theme roles for Interactive Surface, Interactive Surface contributes interaction states, and Layout core applies spatial behavior. ## Public Exports @@ -62,7 +61,7 @@ The UI Style Kit bridge establishes its visual tokens, Interactive Surface contr | `layout-style-css/utilities.css` | Structural utilities and ordering escape hatches. | | `layout-style-css/personalities.css` | All sixteen personalities. | | `layout-style-css/personalities/minimal-saas.css` | One personality; substitute any public personality name. | -| `layout-style-css/integrations/ui-style-kit.css` | Import-free structural bridge for UI Style Kit names. | +| `layout-style-css/integrations/ui-style-kit.css` | Deprecated import-free structural bridge for legacy UI Style Kit structural aliases. | | `layout-style-css/legacy.css` | Full v2 bundle plus v1 compatibility aliases. | The old root-level personality files and `all-with-ui-kit*` aggregates were removed. See [Migrating To 2.0](docs/wiki/Migrating-To-2.0.md) for exact mappings. @@ -159,7 +158,13 @@ These are escape hatches. Visual reordering can diverge from screen-reader readi ## UI Style Kit Naming Compatibility -Import `layout-style-css/integrations/ui-style-kit.css` after the UI and interaction layers. The bridge maps structural aliases such as `.saas-container`, `.bento-grid`, and `.cyber-split` without importing packages or redefining visual tokens. +`layout-style-css/integrations/ui-style-kit.css` remains available for legacy code that still uses UI-prefixed structural aliases such as `.saas-container`, `.bento-grid`, and `.cyber-split`. It is frozen and deprecated; new ecosystem imports should use UI Style Kit visual builds plus Layout core instead of the structural bridge. + +Deprecated bridge import: + +```js +import "layout-style-css/integrations/ui-style-kit.css"; +``` Supported UI prefixes are `saas`, `bento`, `max`, `bau`, `tactile`, `neo`, `retro`, `brutal`, `cyber`, `y2k`, and `rg`. @@ -178,11 +183,11 @@ The v1 `.ly-surface--raised` selector is intentionally removed, including from ` ## CDN ```html - - + + ``` -Companion CDN styles must appear in the same order as the package imports: UI Style Kit bridge, Interactive Surface `state-core.css`, Layout integration bridge, then Layout core. +Companion CDN styles must appear in the same order as the package imports: UI Style Kit visual CSS, UI Style Kit `interactive-surface-theme.css`, Interactive Surface `state-core.css`, then Layout core. Load `layout-style-css/integrations/ui-style-kit.css` only for deprecated structural aliases. ## Development And Verification diff --git a/demo/demo.js b/demo/demo.js index 2e5d1c9..896b5bb 100644 --- a/demo/demo.js +++ b/demo/demo.js @@ -1,3 +1,82 @@ +const UI_STYLE_KIT_VERSION = "2.1.0"; +const UI_STYLE_KIT_MANIFEST_URL = `https://unpkg.com/ui-style-kit-css@${UI_STYLE_KIT_VERSION}/manifest.json`; +const UI_STYLE_KIT_MANIFEST_FALLBACK = Object.freeze({ + version: UI_STYLE_KIT_VERSION, + presets: Object.freeze([ + Object.freeze({ id: "minimal-saas", label: "Minimal SaaS", prefix: "saas" }), + Object.freeze({ id: "bento", label: "Bento", prefix: "bento" }), + Object.freeze({ id: "maximalist", label: "Maximalist", prefix: "max" }), + Object.freeze({ id: "bauhaus", label: "Bauhaus", prefix: "bau" }), + Object.freeze({ id: "tactile", label: "Tactile", prefix: "tactile" }), + Object.freeze({ id: "neumorphism", label: "Neumorphism", prefix: "neo" }), + Object.freeze({ id: "retrofuturism", label: "Retrofuturism", prefix: "retro" }), + Object.freeze({ id: "brutalism", label: "Brutalism", prefix: "brutal" }), + Object.freeze({ id: "cyberpunk", label: "Cyberpunk", prefix: "cyber" }), + Object.freeze({ id: "y2k", label: "Y2K", prefix: "y2k" }), + Object.freeze({ id: "retro-glass", label: "Retro Glass", prefix: "rg" }) + ]), + themes: Object.freeze([ + "midnight-gold", + "ocean-steel", + "forest-moss", + "sunset-ember", + "royal-plum", + "graphite-cyan", + "desert-sage", + "rose-quartz", + "cyber-lime", + "arctic-indigo" + ]), + modes: Object.freeze(["light", "dark", "contrast"]) +}); + +function normalizeUiStyleKitManifest(manifest) { + const normalized = { + version: String(manifest?.version ?? UI_STYLE_KIT_VERSION), + presets: Array.isArray(manifest?.presets) ? manifest.presets : [], + themes: Array.isArray(manifest?.themes) ? manifest.themes : [], + modes: Array.isArray(manifest?.modes) ? manifest.modes : [] + }; + const presets = normalized.presets + .map((preset) => ({ + id: String(preset?.id ?? ""), + label: String(preset?.label ?? preset?.id ?? ""), + prefix: String(preset?.prefix ?? "") + })) + .filter((preset) => preset.id && preset.prefix); + + if (presets.length === 0 || normalized.themes.length === 0 || normalized.modes.length === 0) { + throw new Error("UI Style Kit manifest is missing presets, themes, or modes."); + } + + return Object.freeze({ + version: normalized.version, + presets: Object.freeze(presets.map((preset) => Object.freeze(preset))), + themes: Object.freeze(normalized.themes.map(String)), + modes: Object.freeze(normalized.modes.map(String)) + }); +} + +async function loadUiStyleKitManifest() { + try { + const response = await fetch(UI_STYLE_KIT_MANIFEST_URL, { cache: "force-cache" }); + + if (!response.ok) { + throw new Error(`HTTP ${response.status}`); + } + + return normalizeUiStyleKitManifest(await response.json()); + } catch (error) { + /* + The published demo can render before the companion UI release reaches every CDN edge. + The fallback mirrors the 2.1 manifest contract so layout behavior stays testable. + */ + console.warn("UI Style Kit 2.1 manifest unavailable; using the packaged fallback.", error); + return UI_STYLE_KIT_MANIFEST_FALLBACK; + } +} + +const UI_STYLE_KIT_MANIFEST = await loadUiStyleKitManifest(); const ALLOWLISTS = Object.freeze({ wrapper: Object.freeze(["default", "compact", "prose", "content", "wide", "full", "breakout"]), recipe: Object.freeze([ @@ -29,31 +108,9 @@ const ALLOWLISTS = Object.freeze({ ]), container: Object.freeze(["auto", "40rem", "47rem", "49rem", "63rem", "65rem", "80rem"]), density: Object.freeze(["compact", "comfortable", "spacious"]), - ui: Object.freeze([ - "minimal-saas", - "bento", - "maximalist", - "bauhaus", - "tactile", - "neumorphism", - "retrofuturism", - "brutalism", - "cyberpunk", - "y2k", - "retro-glass" - ]), - theme: Object.freeze([ - "arctic-indigo", - "ocean-steel", - "graphite-cyan", - "forest-moss", - "rose-quartz", - "desert-sage", - "midnight-gold", - "cyber-lime", - "sunset-ember" - ]), - mode: Object.freeze(["light", "dark", "contrast"]), + ui: Object.freeze(UI_STYLE_KIT_MANIFEST.presets.map((preset) => preset.id)), + theme: Object.freeze(UI_STYLE_KIT_MANIFEST.themes), + mode: Object.freeze(UI_STYLE_KIT_MANIFEST.modes), ecosystem: Object.freeze(["layout-only", "layout-ui", "all-three"]) }); @@ -85,19 +142,9 @@ const DENSITY_GAPS = Object.freeze({ spacious: "1.5rem" }); -const UI_CLASS_PREFIXES = Object.freeze({ - "minimal-saas": "saas", - bento: "bento", - maximalist: "max", - bauhaus: "bau", - tactile: "tactile", - neumorphism: "neo", - retrofuturism: "retro", - brutalism: "brutal", - cyberpunk: "cyber", - y2k: "y2k", - "retro-glass": "rg" -}); +const UI_CLASS_PREFIXES = Object.freeze( + Object.fromEntries(UI_STYLE_KIT_MANIFEST.presets.map((preset) => [preset.id, preset.prefix])) +); const RECIPE_CLASSES = Object.freeze({ "app-shell": "ly-app-shell", @@ -120,14 +167,13 @@ const RECIPE_AREAS = Object.freeze({ const ECOSYSTEM_IMPORTS = Object.freeze({ "layout-only": Object.freeze(['import "layout-style-css";']), "layout-ui": Object.freeze([ - 'import "ui-style-kit-css/with-bridge.css";', - 'import "layout-style-css/integrations/ui-style-kit.css";', + 'import "ui-style-kit-css/visual.css";', 'import "layout-style-css";' ]), "all-three": Object.freeze([ - 'import "ui-style-kit-css/with-bridge.css";', + 'import "ui-style-kit-css/visual.css";', + 'import "ui-style-kit-css/interactive-surface-theme.css";', 'import "interactive-surface-css/state-core.css";', - 'import "layout-style-css/integrations/ui-style-kit.css";', 'import "layout-style-css";' ]) }); @@ -152,6 +198,7 @@ const copyStatus = document.querySelector("#copyStatus"); const ecosystemStatus = document.querySelector("#ecosystemStatus"); const containerReadout = document.querySelector("#containerReadout"); const uiKitStylesheet = document.querySelector("#uiKitStylesheet"); +const uiKitInteractiveThemeStylesheet = document.querySelector("#uiKitInteractiveThemeStylesheet"); const interactiveSurfaceStylesheet = document.querySelector("#interactiveSurfaceStylesheet"); const layoutIntegrationStylesheet = document.querySelector("#layoutIntegrationStylesheet"); const layoutCoreStylesheet = document.querySelector("#layoutCoreStylesheet"); @@ -162,6 +209,9 @@ const drawerBackdrop = document.querySelector("#demoControlsBackdrop"); const stateToggle = document.querySelector("#stateToggle"); const mobileControlsQuery = window.matchMedia("(max-width: 63.999rem)"); +body.dataset.uiManifestVersion = UI_STYLE_KIT_MANIFEST.version; +syncUiManifestSelectOptions(); + let state = readStateFromQuery(); let drawerReturnFocus = null; let querySyncTimer = null; @@ -302,6 +352,40 @@ function formatLabel(value) { .join(" "); } +function syncUiManifestSelectOptions() { + const optionGroups = { + ui: UI_STYLE_KIT_MANIFEST.presets.map((preset) => ({ + value: preset.id, + label: preset.label || formatLabel(preset.id) + })), + theme: UI_STYLE_KIT_MANIFEST.themes.map((theme) => ({ + value: theme, + label: formatLabel(theme) + })), + mode: UI_STYLE_KIT_MANIFEST.modes.map((mode) => ({ + value: mode, + label: mode === "contrast" ? "High contrast" : formatLabel(mode) + })) + }; + + for (const [key, options] of Object.entries(optionGroups)) { + const select = controls[key]; + + if (!select) { + continue; + } + + select.replaceChildren( + ...options.map(({ value, label }) => { + const option = document.createElement("option"); + option.value = value; + option.textContent = label; + return option; + }) + ); + } +} + function syncUiKitClasses() { const prefix = UI_CLASS_PREFIXES[state.ui]; @@ -330,8 +414,9 @@ function syncEcosystem() { const includesInteractiveSurface = state.ecosystem === "all-three"; uiKitStylesheet.disabled = !includesUi; + uiKitInteractiveThemeStylesheet.disabled = !includesInteractiveSurface; interactiveSurfaceStylesheet.disabled = !includesInteractiveSurface; - layoutIntegrationStylesheet.disabled = !includesUi; + layoutIntegrationStylesheet.disabled = true; layoutCoreStylesheet.disabled = false; ecosystemStatus.textContent = ECOSYSTEM_LABELS[state.ecosystem]; } diff --git a/demo/index.html b/demo/index.html index 224fe2a..1bb82a7 100644 --- a/demo/index.html +++ b/demo/index.html @@ -5,8 +5,8 @@ - - + + @@ -36,7 +36,7 @@ - + @@ -48,11 +48,11 @@ - - + + - + @@ -65,11 +65,12 @@ - layout-style-css 2.0 Interactive Layout Lab + layout-style-css 2.1 Interactive Layout Lab - - - + + + + @@ -78,7 +79,7 @@ "@context": "https://schema.org", "@type": "SoftwareSourceCode", "name": "layout-style-css", - "version": "2.0.0", + "version": "2.1.0", "description": "A dependency-free, container-first CSS layout library for semantic wrappers, composition primitives, responsive recipes, and spatial personalities.", "programmingLanguage": "CSS", "applicationCategory": "DeveloperApplication", @@ -97,7 +98,7 @@
- layout-style-css 2.0 + layout-style-css 2.1 Interactive Layout Lab
@@ -206,15 +207,16 @@

Layout controls

@@ -246,7 +248,7 @@

Compose for the space a component actually has.

- Interactive Surface 1.4.0 state + Interactive Surface 1.5.0 state diff --git a/demo/site.webmanifest b/demo/site.webmanifest index 1342cbe..13e4d02 100644 --- a/demo/site.webmanifest +++ b/demo/site.webmanifest @@ -1,7 +1,7 @@ { "name": "layout-style-css", "short_name": "Layout CSS", - "description": "Container-first layout-style-css 2.0 lab for wrappers, recipes, personalities, and optional ecosystem integrations.", + "description": "Container-first layout-style-css 2.1 lab for wrappers, recipes, personalities, and optional ecosystem integrations.", "start_url": "./index.html", "display": "standalone", "background_color": "#111827", diff --git a/docs/wiki/Demo-And-GitHub-Pages.md b/docs/wiki/Demo-And-GitHub-Pages.md index b5dfffd..69d8ffe 100644 --- a/docs/wiki/Demo-And-GitHub-Pages.md +++ b/docs/wiki/Demo-And-GitHub-Pages.md @@ -37,7 +37,7 @@ The smoke suite verifies: - bounded scrolling - accessible mobile controls - layout-only, Layout plus UI, and all-three ecosystem modes -- pinned `ui-style-kit-css@2.0.1` and `interactive-surface-css@1.4.0` fixture behavior +- pinned `ui-style-kit-css@2.1.0` visual, token bridge, and manifest behavior plus released `interactive-surface-css@1.5.0` The demo query parser uses explicit allowlists and writes generated imports and markup with `textContent`. diff --git a/docs/wiki/Getting-Started.md b/docs/wiki/Getting-Started.md index 7c4773f..784a67c 100644 --- a/docs/wiki/Getting-Started.md +++ b/docs/wiki/Getting-Started.md @@ -7,7 +7,7 @@ Layout Style CSS 2.0 provides structural layout without prescribing theme or com Node.js 20 or newer is required for development scripts. ```bash -npm install layout-style-css@2.0.0 +npm install layout-style-css@2.1.0 ``` ## Import diff --git a/docs/wiki/Home.md b/docs/wiki/Home.md index 95d524c..eda0cea 100644 --- a/docs/wiki/Home.md +++ b/docs/wiki/Home.md @@ -1,6 +1,6 @@ # Layout Style CSS Wiki -Version 2.0.0 is a container-first, dependency-free layout library. It ships semantic wrappers, composition primitives, seven recipes, sixteen layout personalities, focused exports, and a v2-only compatibility bundle. +Version 2.1.0 is a container-first, dependency-free layout library. It ships semantic wrappers, composition primitives, seven recipes, sixteen layout personalities, focused exports, a v2-only compatibility bundle, and refreshed ecosystem fixtures for UI Style Kit CSS 2.1 and Interactive Surface CSS 1.5. ## Start Here @@ -22,7 +22,7 @@ Version 2.0.0 is a container-first, dependency-free layout library. It ships sem Layout Style CSS owns spatial behavior: containment, wrappers, flow, grids, areas, spans, sizing, and responsive arrangement. UI Style Kit owns visual paint. Interactive Surface owns interaction-state styling. -The package has no runtime or peer dependencies. `ui-style-kit-css@2.0.1` and `interactive-surface-css@1.4.0` are development fixtures used to prove optional integration. +The package has no runtime or peer dependencies. `ui-style-kit-css@2.1.0` and `interactive-surface-css@1.5.0` are development fixtures used to prove optional integration; UI Style Kit is resolved from the sibling staged checkout until its approved 2.1.0 publication. ## Supported Baseline @@ -30,4 +30,4 @@ The package has no runtime or peer dependencies. `ui-style-kit-css@2.0.1` and `i - Current evergreen Chromium, Firefox, and WebKit - Mobile-first DOM order with core thresholds at `48rem` and `64rem`, plus tested personality-specific overrides -The UI Style Kit revision is a documented follow-up, not part of this 2.0 release. +The deprecated UI structural bridge remains available for existing UI-prefixed layout aliases, but canonical 2.1 examples use UI Style Kit visual CSS, the UI token bridge, Interactive Surface `state-core.css`, and Layout core. diff --git a/docs/wiki/Installation-And-CDN.md b/docs/wiki/Installation-And-CDN.md index 33a6505..b2aaaa3 100644 --- a/docs/wiki/Installation-And-CDN.md +++ b/docs/wiki/Installation-And-CDN.md @@ -5,7 +5,7 @@ Layout Style CSS 2.0 is dependency-free and has no peer dependency contract. Nod ## npm ```bash -npm install layout-style-css@2.0.0 +npm install layout-style-css@2.1.0 ``` ## Focused Exports @@ -37,32 +37,31 @@ import "layout-style-css"; Layout plus UI Style Kit: ```js -import "ui-style-kit-css/with-bridge.css"; -import "layout-style-css/integrations/ui-style-kit.css"; +import "ui-style-kit-css/visual.css"; import "layout-style-css"; ``` All three libraries, in required order: ```js -import "ui-style-kit-css/with-bridge.css"; +import "ui-style-kit-css/visual.css"; +import "ui-style-kit-css/interactive-surface-theme.css"; import "interactive-surface-css/state-core.css"; -import "layout-style-css/integrations/ui-style-kit.css"; import "layout-style-css"; ``` -`ui-style-kit-css@2.0.1` and `interactive-surface-css@1.4.0` are tested integration fixtures, not runtime dependencies. The next UI Style Kit revision is a follow-up release. +The first import block is deprecated compatibility for legacy UI-prefixed structural aliases. Canonical 2.1 imports use `ui-style-kit-css@2.1.0` visual CSS, `ui-style-kit-css/interactive-surface-theme.css`, `interactive-surface-css@1.5.0` state core, and Layout core. UI Style Kit is a staged local fixture until its approved 2.1.0 publication; Interactive Surface 1.5.0 is released. ## CDN Layout only: ```html - - + + ``` -For optional companions, preserve the same order as the package imports: UI Style Kit bridge, Interactive Surface 1.4.0 `state-core.css`, Layout integration bridge, then Layout core. +For optional companions, preserve the same order as the package imports: UI Style Kit visual CSS, UI Style Kit `interactive-surface-theme.css`, Interactive Surface 1.5.0 `state-core.css`, then Layout core. Load `layout-style-css/integrations/ui-style-kit.css` only for deprecated structural aliases. ## Browser Baseline diff --git a/docs/wiki/Migrating-To-2.0.md b/docs/wiki/Migrating-To-2.0.md index 8a2321a..abe9684 100644 --- a/docs/wiki/Migrating-To-2.0.md +++ b/docs/wiki/Migrating-To-2.0.md @@ -6,18 +6,18 @@ Version 2.0 rebuilds Layout Style CSS as a container-first, dependency-free stru - Development and release scripts require Node.js 20 or newer. - The package has no runtime dependencies and no peer dependencies. -- `ui-style-kit-css@2.0.1` and `interactive-surface-css@1.4.0` are exact development and integration fixtures, not consumer dependencies. +- `ui-style-kit-css@2.1.0` and `interactive-surface-css@1.5.0` are development and integration fixtures, not consumer dependencies. - Current evergreen Chromium, Firefox, and WebKit are supported. - Responsive recipes use core thresholds at `48rem` and `64rem`; personalities may add personality-specific thresholds. Install companions explicitly only when the application uses them: ```bash -npm install layout-style-css@2.0.0 -npm install ui-style-kit-css@2.0.1 interactive-surface-css@1.4.0 +npm install layout-style-css@2.1.0 +npm install ui-style-kit-css@2.1.0 interactive-surface-css@1.5.0 ``` -The upcoming UI Style Kit revision is a separate follow-up, not part of the Layout Style CSS 2.0 release. +Layout Style CSS 2.1 keeps the v2 layout contract and updates the optional companion fixture path to UI Style Kit visual CSS, its Interactive Surface token bridge, and Interactive Surface 1.5 state core. ## Export Changes @@ -60,7 +60,7 @@ import "layout-style-css"; Layout plus UI Style Kit: ```js -import "ui-style-kit-css/with-bridge.css"; +import "ui-style-kit-css/visual.css"; import "layout-style-css/integrations/ui-style-kit.css"; import "layout-style-css"; ``` @@ -68,9 +68,9 @@ import "layout-style-css"; All three libraries must be imported in this exact order: ```js -import "ui-style-kit-css/with-bridge.css"; +import "ui-style-kit-css/visual.css"; +import "ui-style-kit-css/interactive-surface-theme.css"; import "interactive-surface-css/state-core.css"; -import "layout-style-css/integrations/ui-style-kit.css"; import "layout-style-css"; ``` diff --git a/docs/wiki/Release-And-Publishing.md b/docs/wiki/Release-And-Publishing.md index 832bc69..ffdb37e 100644 --- a/docs/wiki/Release-And-Publishing.md +++ b/docs/wiki/Release-And-Publishing.md @@ -1,10 +1,10 @@ # Release And Publishing -This checklist prepares `layout-style-css@2.0.0`. Publishing, tagging, pushing, and creating a GitHub release require separate explicit approval. +This checklist prepares `layout-style-css@2.1.0`. Publishing, tagging, pushing, and creating a GitHub release require separate explicit approval. ## Version Contract -- `package.json` and `package-lock.json` identify `2.0.0`. +- `package.json` and `package-lock.json` identify `2.1.0`. - Node.js 20 and 22 pass standalone CI. - `CHANGELOG.md` contains the dated breaking-release entry. - README, migration guide, wiki, demo, exports, and tarball describe the same v2 API. @@ -37,22 +37,22 @@ git diff --check ## Tag And Version Validation -The publish workflow checks out the selected tag and fails unless it equals `v${package.version}`. For this release the only valid tag is `v2.0.0`. +The publish workflow checks out the selected tag and fails unless it equals `v${package.version}`. For this release the only valid tag is `v2.1.0`. After separate approval, an operator may check registry availability: ```bash -npm view layout-style-css@2.0.0 version --json +npm view layout-style-css@2.1.0 version --json ``` The eventual release sequence is: ```bash -git tag v2.0.0 -git push origin v2.0.0 +git tag v2.1.0 +git push origin v2.1.0 ``` -Publishing the `v2.0.0` GitHub release triggers the npm workflow. A separately approved recovery run may use `release_tag` set to `v2.0.0`. +Publishing the `v2.1.0` GitHub release triggers the npm workflow. A separately approved recovery run may use `release_tag` set to `v2.1.0`. ## Workflow Safety diff --git a/docs/wiki/UI-Style-Kit-Compatibility.md b/docs/wiki/UI-Style-Kit-Compatibility.md index 28c34cd..fbcba1d 100644 --- a/docs/wiki/UI-Style-Kit-Compatibility.md +++ b/docs/wiki/UI-Style-Kit-Compatibility.md @@ -1,6 +1,6 @@ # UI Style Kit Compatibility -Layout Style CSS is dependency-free. `ui-style-kit-css@2.0.1` is an exact development fixture used to verify the optional bridge; it is not a runtime or peer dependency. The UI Style Kit revision is a follow-up, not part of this release. +Layout Style CSS is dependency-free. `ui-style-kit-css@2.1.0` is the visual fixture for 2.1 ecosystem verification; it is resolved from the sibling staged checkout until its approved publication and is not a runtime or peer dependency. ## Ownership Boundary @@ -15,21 +15,20 @@ Layout Style CSS is dependency-free. `ui-style-kit-css@2.0.1` is an exact develo Layout plus UI: ```js -import "ui-style-kit-css/with-bridge.css"; -import "layout-style-css/integrations/ui-style-kit.css"; +import "ui-style-kit-css/visual.css"; import "layout-style-css"; ``` All three libraries: ```js -import "ui-style-kit-css/with-bridge.css"; +import "ui-style-kit-css/visual.css"; +import "ui-style-kit-css/interactive-surface-theme.css"; import "interactive-surface-css/state-core.css"; -import "layout-style-css/integrations/ui-style-kit.css"; import "layout-style-css"; ``` -The Layout integration bridge contains structural mappings but no package imports. The removed `all-with-ui-kit*` aggregates have no v2 replacement because dependency ownership stays with the application. +The Layout integration bridge contains structural mappings but no package imports. It remains available as deprecated compatibility for legacy UI-prefixed structural aliases. The removed `all-with-ui-kit*` aggregates have no v2 replacement because dependency ownership stays with the application. ## Structural Aliases @@ -43,8 +42,8 @@ The removed v1 `.ly-surface--raised` selector is not restored by `legacy.css`; i | Library | Verified fixture | Consumer requirement | | --- | --- | --- | -| Layout Style CSS | `2.0.0` | Required for this API | -| UI Style Kit CSS | `2.0.1` | Optional | -| Interactive Surface CSS | `1.4.0` `state-core.css` | Optional | +| Layout Style CSS | `2.1.0` | Required for this API | +| UI Style Kit CSS | `2.1.0` visual CSS and manifest | Optional staged fixture | +| Interactive Surface CSS | `1.5.0` `state-core.css` | Optional released fixture | Current evergreen Chromium, Firefox, and WebKit are covered by the release gate. diff --git a/package-lock.json b/package-lock.json index 59575cf..fef7b88 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,18 +1,33 @@ { "name": "layout-style-css", - "version": "2.0.0", + "version": "2.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "layout-style-css", - "version": "2.0.0", + "version": "2.1.0", "license": "MIT", "devDependencies": { "@playwright/test": "1.61.1", - "interactive-surface-css": "1.4.0", + "interactive-surface-css": "1.5.0", "stylelint": "17.14.0", - "ui-style-kit-css": "2.0.1" + "ui-style-kit-css": "file:../ui-style-kit-css" + }, + "engines": { + "node": ">=20" + } + }, + "../ui-style-kit-css": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "devDependencies": { + "@axe-core/playwright": "4.12.1", + "@playwright/test": "~1.61.1", + "css-tree": "3.2.1", + "lightningcss": "1.33.0", + "stylelint": "~17.14.0" }, "engines": { "node": ">=20" @@ -848,9 +863,9 @@ "license": "ISC" }, "node_modules/interactive-surface-css": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/interactive-surface-css/-/interactive-surface-css-1.4.0.tgz", - "integrity": "sha512-m42ojieEhGJvB/U5wg5DsAbVQYddrD6+J0Ky9E1yqZ4Z39N+RGxrw1uYLsURl2TAIWv/MhAgEMZH257EdrNsSA==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/interactive-surface-css/-/interactive-surface-css-1.5.0.tgz", + "integrity": "sha512-eyNNUNi4X57CY5O8GXdl8f1pbUyKjgEsKZBbkicUFZENn+sZ1sYbBpVLhfTQDyj3xQwZe5s1yFI/ZvauI3wQBw==", "dev": true, "license": "MIT", "engines": { @@ -1605,14 +1620,8 @@ } }, "node_modules/ui-style-kit-css": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ui-style-kit-css/-/ui-style-kit-css-2.0.1.tgz", - "integrity": "sha512-yn3L6MfKJpIPbmueiJzpWSv5U7ZIU95ABXK+ltJU34qP38CnMSrImyYKK140M6fAgvbe08kHO4+fftC4z3dlTQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=20" - } + "resolved": "../ui-style-kit-css", + "link": true }, "node_modules/unicorn-magic": { "version": "0.4.0", diff --git a/package.json b/package.json index 45a9e02..8dbd06e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "layout-style-css", - "version": "2.0.0", + "version": "2.1.0", "description": "Dependency-free structural CSS for responsive wrappers, composition primitives, recipes, utilities, and layout personalities.", "type": "module", "license": "MIT", @@ -54,9 +54,9 @@ }, "devDependencies": { "@playwright/test": "1.61.1", - "interactive-surface-css": "1.4.0", + "interactive-surface-css": "1.5.0", "stylelint": "17.14.0", - "ui-style-kit-css": "2.0.1" + "ui-style-kit-css": "file:../ui-style-kit-css" }, "exports": { ".": "./dist/layout-style-css.css", diff --git a/test/demo-smoke.test.mjs b/test/demo-smoke.test.mjs index ef516b7..ff23ba0 100644 --- a/test/demo-smoke.test.mjs +++ b/test/demo-smoke.test.mjs @@ -12,27 +12,32 @@ const demoCssPath = join(root, "demo", "demo.css"); const demoJsPath = join(root, "demo", "demo.js"); const packagePath = join(root, "package.json"); const testTempDir = join(root, ".tmp", "playwright"); -const uiKitCssPath = join( - root, - "node_modules", - "ui-style-kit-css", - "dist", - "ui-style-kit.with-bridge.min.css" -); +const uiKitPackageDir = join(root, "node_modules", "ui-style-kit-css"); +const uiKitManifestPath = join(uiKitPackageDir, "manifest.json"); +const uiKitVisualCssPath = join(uiKitPackageDir, "dist", "ui-style-kit.visual.min.css"); +const uiKitInteractiveThemeCssPath = join(uiKitPackageDir, "styles", "interactive-surface-theme.css"); const interactiveSurfaceCssPath = join( root, "node_modules", "interactive-surface-css", "state-core.css" ); -const uiKitUrl = - "https://unpkg.com/ui-style-kit-css@2.0.1/dist/ui-style-kit.with-bridge.min.css"; +const uiKitVisualUrl = + "https://unpkg.com/ui-style-kit-css@2.1.0/dist/ui-style-kit.visual.min.css"; +const uiKitInteractiveThemeUrl = + "https://unpkg.com/ui-style-kit-css@2.1.0/styles/interactive-surface-theme.css"; +const uiKitManifestUrl = "https://unpkg.com/ui-style-kit-css@2.1.0/manifest.json"; const interactiveSurfaceUrl = - "https://unpkg.com/interactive-surface-css@1.4.0/state-core.css"; + "https://unpkg.com/interactive-surface-css@1.5.0/state-core.css"; const fixtureIntegrity = (path) => `sha384-${createHash("sha384").update(readFileSync(path)).digest("base64")}`; -const uiKitIntegrity = fixtureIntegrity(uiKitCssPath); +const uiKitVisualIntegrity = fixtureIntegrity(uiKitVisualCssPath); +const uiKitInteractiveThemeIntegrity = fixtureIntegrity(uiKitInteractiveThemeCssPath); const interactiveSurfaceIntegrity = fixtureIntegrity(interactiveSurfaceCssPath); +const uiKitManifest = JSON.parse(readFileSync(uiKitManifestPath, "utf8")); +const uiKitPresetIds = uiKitManifest.presets.map((preset) => preset.id); +const uiKitThemeIds = uiKitManifest.themes; +const uiKitModeIds = uiKitManifest.modes; const recipes = [ "app-shell", @@ -67,31 +72,9 @@ const controlValues = { personalitySelect: personalities, containerSelect: ["auto", "40rem", "47rem", "49rem", "63rem", "65rem", "80rem"], densitySelect: ["compact", "comfortable", "spacious"], - uiSelect: [ - "minimal-saas", - "bento", - "maximalist", - "bauhaus", - "tactile", - "neumorphism", - "retrofuturism", - "brutalism", - "cyberpunk", - "y2k", - "retro-glass" - ], - themeSelect: [ - "arctic-indigo", - "ocean-steel", - "graphite-cyan", - "forest-moss", - "rose-quartz", - "desert-sage", - "midnight-gold", - "cyber-lime", - "sunset-ember" - ], - modeSelect: ["light", "dark", "contrast"], + uiSelect: uiKitPresetIds, + themeSelect: uiKitThemeIds, + modeSelect: uiKitModeIds, ecosystemSelect: ["layout-only", "layout-ui", "all-three"] }; const recipeAreas = { @@ -291,10 +274,15 @@ function assertStaticContract() { assert(existsSync(demoPath), "Demo smoke test requires demo/index.html"); assert(existsSync(demoCssPath), "The v2 demo must move authored styles into demo/demo.css"); assert(existsSync(demoJsPath), "The v2 demo must move behavior into demo/demo.js"); - assert(existsSync(uiKitCssPath), "Demo smoke test requires ui-style-kit-css@2.0.1"); + assert(existsSync(uiKitManifestPath), "Demo smoke test requires the UI Style Kit 2.1 manifest"); + assert(existsSync(uiKitVisualCssPath), "Demo smoke test requires the UI Style Kit 2.1 visual bundle"); + assert( + existsSync(uiKitInteractiveThemeCssPath), + "Demo smoke test requires the UI Style Kit 2.1 Interactive Surface token bridge" + ); assert( existsSync(interactiveSurfaceCssPath), - "Demo smoke test requires interactive-surface-css@1.4.0" + "Demo smoke test requires interactive-surface-css@1.5.0" ); const html = readFileSync(demoPath, "utf8"); @@ -345,6 +333,7 @@ function assertStaticContract() { const stylesheetIds = [ "uiKitStylesheet", + "uiKitInteractiveThemeStylesheet", "interactiveSurfaceStylesheet", "layoutIntegrationStylesheet", "layoutCoreStylesheet" @@ -354,12 +343,14 @@ function assertStaticContract() { assert.deepEqual( [...stylesheetPositions].sort((a, b) => a - b), stylesheetPositions, - "Ecosystem stylesheet DOM order must be UI, Interactive Surface, integration, then core" + "Ecosystem stylesheet DOM order must be UI visual, UI token bridge, Interactive Surface, deprecated integration, then core" ); - assert(html.includes(uiKitUrl), "Demo must pin ui-style-kit-css@2.0.1"); - assert(html.includes(interactiveSurfaceUrl), "Demo must pin interactive-surface-css@1.4.0"); + assert(html.includes(uiKitVisualUrl), "Demo must pin the UI Style Kit 2.1 visual bundle"); + assert(html.includes(uiKitInteractiveThemeUrl), "Demo must pin the UI Style Kit 2.1 token bridge"); + assert(html.includes(interactiveSurfaceUrl), "Demo must pin interactive-surface-css@1.5.0"); for (const [id, integrity] of [ - ["uiKitStylesheet", uiKitIntegrity], + ["uiKitStylesheet", uiKitVisualIntegrity], + ["uiKitInteractiveThemeStylesheet", uiKitInteractiveThemeIntegrity], ["interactiveSurfaceStylesheet", interactiveSurfaceIntegrity] ]) { const link = html.match(new RegExp(`]*id="${id}"[^>]*>`))?.[0] ?? ""; @@ -376,6 +367,20 @@ function assertStaticContract() { assert(html.includes("data-ly-area="), "Demo must use canonical area hooks"); assert(script.includes("const ALLOWLISTS = Object.freeze"), "Query state must use explicit allowlists"); + assert( + script.includes('const UI_STYLE_KIT_VERSION = "2.1.0"') && + script.includes("UI_STYLE_KIT_MANIFEST_URL") && + script.includes("/manifest.json"), + "Demo must load the UI Style Kit 2.1 manifest" + ); + assert( + script.includes("UI_STYLE_KIT_MANIFEST_FALLBACK"), + "Demo must keep a bounded manifest fallback for unpublished or stale CDN edges" + ); + assert( + script.includes("syncUiManifestSelectOptions"), + "Demo controls must derive UI options from the UI Style Kit manifest contract" + ); assert(script.includes("URLSearchParams"), "Demo must restore and synchronize query state"); assert(!script.includes("innerHTML"), "Demo JavaScript must never interpolate with innerHTML"); assert( @@ -462,13 +467,15 @@ async function verifyQueryAndEcosystem(page, baseUrl) { body: { layout: document.body.dataset.lyLayout, density: document.body.dataset.density, - ecosystem: document.body.dataset.ecosystem + ecosystem: document.body.dataset.ecosystem, + uiManifestVersion: document.body.dataset.uiManifestVersion }, previewLayout: document.querySelector("#previewRoot")?.dataset.lyLayout, wrapperClass: document.querySelector("#previewWrapper")?.className, recipe: document.querySelector("#recipePreview")?.dataset.lyRecipe, links: [ document.querySelector("#uiKitStylesheet")?.disabled, + document.querySelector("#uiKitInteractiveThemeStylesheet")?.disabled, document.querySelector("#interactiveSurfaceStylesheet")?.disabled, document.querySelector("#layoutIntegrationStylesheet")?.disabled, document.querySelector("#layoutCoreStylesheet")?.disabled @@ -491,18 +498,19 @@ async function verifyQueryAndEcosystem(page, baseUrl) { assert.deepEqual(restored.body, { layout: "synthwave", density: "compact", - ecosystem: "all-three" + ecosystem: "all-three", + uiManifestVersion: "2.1.0" }); assert.equal(restored.previewLayout, "synthwave"); assert.match(restored.wrapperClass, /\bly-wrapper--prose\b/); assert.equal(restored.recipe, "docs"); - assert.deepEqual(restored.links, [false, false, false, false]); + assert.deepEqual(restored.links, [false, false, false, true, false]); assert.equal( restored.imports, [ - 'import "ui-style-kit-css/with-bridge.css";', + 'import "ui-style-kit-css/visual.css";', + 'import "ui-style-kit-css/interactive-surface-theme.css";', 'import "interactive-surface-css/state-core.css";', - 'import "layout-style-css/integrations/ui-style-kit.css";', 'import "layout-style-css";' ].join("\n") ); @@ -562,43 +570,54 @@ async function verifyQueryAndEcosystem(page, baseUrl) { assert.deepEqual( await page.evaluate(() => [ document.querySelector("#uiKitStylesheet")?.disabled, + document.querySelector("#uiKitInteractiveThemeStylesheet")?.disabled, document.querySelector("#interactiveSurfaceStylesheet")?.disabled, document.querySelector("#layoutIntegrationStylesheet")?.disabled, document.querySelector("#layoutCoreStylesheet")?.disabled ]), - [true, true, true, false] + [true, true, true, true, false] ); await page.selectOption("#ecosystemSelect", "layout-ui"); assert.deepEqual( await page.evaluate(() => [ document.querySelector("#uiKitStylesheet")?.disabled, + document.querySelector("#uiKitInteractiveThemeStylesheet")?.disabled, document.querySelector("#interactiveSurfaceStylesheet")?.disabled, document.querySelector("#layoutIntegrationStylesheet")?.disabled, document.querySelector("#layoutCoreStylesheet")?.disabled ]), - [false, true, false, false] + [false, true, true, true, false] ); await page.selectOption("#ecosystemSelect", "all-three"); const stateBefore = await page.locator("#stateToggle").evaluate((element) => ({ pressed: element.getAttribute("aria-pressed"), - opacity: Number.parseFloat(getComputedStyle(element, "::before").opacity) + layerOpacity: Number.parseFloat(getComputedStyle(element, "::before").opacity), + translate: getComputedStyle(element).translate, + boxShadow: getComputedStyle(element).boxShadow })); await page.click("#stateToggle"); - await page.waitForFunction(() => { - const element = document.querySelector("#stateToggle"); - return element && Number.parseFloat(getComputedStyle(element, "::before").opacity) > 0; - }); + await page.waitForFunction( + () => document.querySelector("#stateToggle")?.getAttribute("aria-pressed") === "true" + ); + await page.waitForTimeout(120); const stateAfter = await page.locator("#stateToggle").evaluate((element) => ({ pressed: element.getAttribute("aria-pressed"), - opacity: Number.parseFloat(getComputedStyle(element, "::before").opacity) + layerOpacity: Number.parseFloat(getComputedStyle(element, "::before").opacity), + translate: getComputedStyle(element).translate, + boxShadow: getComputedStyle(element).boxShadow })); assert.equal(stateBefore.pressed, "false"); assert.equal(stateAfter.pressed, "true"); assert( - stateAfter.opacity > stateBefore.opacity, - `All-three active state should be visibly stronger; ${stateBefore.opacity} -> ${stateAfter.opacity}` + stateAfter.layerOpacity !== stateBefore.layerOpacity || + stateAfter.translate !== stateBefore.translate || + stateAfter.boxShadow !== stateBefore.boxShadow, + `All-three active state should resolve a visible state change; ${JSON.stringify({ + before: stateBefore, + after: stateAfter + })}` ); await page.click("#copyImports"); @@ -1283,13 +1302,27 @@ page.on("console", (message) => { page.on("pageerror", (error) => pageErrors.push(error.message)); const corsHeaders = { "access-control-allow-origin": "*" }; -await page.route(uiKitUrl, (route) => +await page.route(uiKitVisualUrl, (route) => route.fulfill({ - path: uiKitCssPath, + path: uiKitVisualCssPath, contentType: "text/css", headers: corsHeaders }) ); +await page.route(uiKitInteractiveThemeUrl, (route) => + route.fulfill({ + path: uiKitInteractiveThemeCssPath, + contentType: "text/css", + headers: corsHeaders + }) +); +await page.route(uiKitManifestUrl, (route) => + route.fulfill({ + path: uiKitManifestPath, + contentType: "application/json", + headers: corsHeaders + }) +); await page.route(interactiveSurfaceUrl, (route) => route.fulfill({ path: interactiveSurfaceCssPath, diff --git a/test/layout-css-contract.test.mjs b/test/layout-css-contract.test.mjs index bc314cf..2895fa8 100644 --- a/test/layout-css-contract.test.mjs +++ b/test/layout-css-contract.test.mjs @@ -602,10 +602,13 @@ function isLocalMarkdownLink(link) { const packageJson = JSON.parse(readFileSync(join(root, "package.json"), "utf8")); const packageLock = JSON.parse(readFileSync(join(root, "package-lock.json"), "utf8")); const lockRoot = packageLock.packages[""]; +const uiKitFixturePackage = JSON.parse( + readFileSync(join(root, "node_modules", "ui-style-kit-css", "package.json"), "utf8") +); const v2ContractFailures = []; -if (packageJson.version !== "2.0.0") { - v2ContractFailures.push(`package version is ${packageJson.version}, expected 2.0.0`); +if (packageJson.version !== "2.1.0") { + v2ContractFailures.push(`package version is ${packageJson.version}, expected 2.1.0`); } if (packageJson.engines?.node !== ">=20") { v2ContractFailures.push(`Node engine is ${packageJson.engines?.node ?? "missing"}, expected >=20`); @@ -613,8 +616,8 @@ if (packageJson.engines?.node !== ">=20") { if (packageJson.dependencies !== undefined || packageJson.peerDependencies !== undefined) { v2ContractFailures.push("runtime and peer dependencies must be absent"); } -if (packageJson.devDependencies?.["interactive-surface-css"] !== "1.4.0") { - v2ContractFailures.push("interactive-surface-css dev fixture must be exactly 1.4.0"); +if (packageJson.devDependencies?.["interactive-surface-css"] !== "1.5.0") { + v2ContractFailures.push("interactive-surface-css dev fixture must be exactly 1.5.0"); } if (JSON.stringify(packageJson.exports) !== JSON.stringify(expectedV2Exports)) { v2ContractFailures.push("package exports do not match the focused v2 contract"); @@ -1205,7 +1208,7 @@ assert(!minified.includes("\n\n"), "Minified bundle should not preserve expanded assert(!minified.includes("@import"), "Minified bundle must be layout-only and flattened"); assert.equal(packageJson.name, "layout-style-css"); -assert.equal(packageJson.version, "2.0.0"); +assert.equal(packageJson.version, "2.1.0"); assert.equal(packageJson.license, "MIT"); assert.equal(packageJson.private, undefined); assert.match(packageJson.description, /dependency-free/i); @@ -1220,21 +1223,32 @@ assert.equal(packageJson.jsdelivr, "dist/layout-style-css.min.css"); assert.equal(packageJson.dependencies, undefined); assert.equal(packageJson.peerDependencies, undefined); assert.equal(packageJson.peerDependenciesMeta, undefined); -assert.equal(packageJson.devDependencies["ui-style-kit-css"], "2.0.1"); -assert.equal(packageJson.devDependencies["interactive-surface-css"], "1.4.0"); +assert.equal(packageJson.devDependencies["ui-style-kit-css"], "file:../ui-style-kit-css"); +assert.equal(packageJson.devDependencies["interactive-surface-css"], "1.5.0"); assert(packageJson.devDependencies.stylelint, "Stylelint must be installed for CSS linting"); assert(packageJson.devDependencies["@playwright/test"], "Playwright must be installed for demo smoke checks"); -assert.equal(packageLock.version, "2.0.0"); -assert.equal(lockRoot.version, "2.0.0"); +assert.equal(packageLock.version, "2.1.0"); +assert.equal(lockRoot.version, "2.1.0"); assert.equal(lockRoot.engines.node, ">=20"); assert.equal(lockRoot.dependencies, undefined); assert.equal(lockRoot.peerDependencies, undefined); assert.equal(lockRoot.peerDependenciesMeta, undefined); -assert.equal(lockRoot.devDependencies["ui-style-kit-css"], "2.0.1"); -assert.equal(lockRoot.devDependencies["interactive-surface-css"], "1.4.0"); +assert.equal(lockRoot.devDependencies["ui-style-kit-css"], "file:../ui-style-kit-css"); +assert.equal(lockRoot.devDependencies["interactive-surface-css"], "1.5.0"); +assert.equal(uiKitFixturePackage.version, "2.1.0", "Local UI Style Kit fixture must be the staged 2.1.0 line"); +assert.equal( + packageLock.packages["node_modules/ui-style-kit-css"].resolved, + "../ui-style-kit-css", + "Lockfile must resolve the staged local UI Style Kit fixture until 2.1.0 is published" +); +assert.equal( + packageLock.packages["node_modules/ui-style-kit-css"].link, + true, + "Lockfile must represent UI Style Kit as a local staged fixture" +); assert.equal( packageLock.packages["node_modules/interactive-surface-css"].version, - "1.4.0", + "1.5.0", "Lockfile must resolve the exact Interactive Surface fixture" ); assert.deepEqual(packageJson.files, [ @@ -1305,6 +1319,7 @@ for (const name of personalityNames) { } const demo = readFileSync(join(root, "demo", "index.html"), "utf8"); +const demoScript = readFileSync(join(root, "demo", "demo.js"), "utf8"); const demoInlineCss = demo.match(/