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..dff029b 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 released registry fixtures for optional ecosystem verification. 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
+ 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..df094de 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 released registry fixtures used to prove optional integration.
## 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..1cb4ad8 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 the released `ui-style-kit-css@2.1.0` visual CSS, `ui-style-kit-css/interactive-surface-theme.css`, released `interactive-surface-css@1.5.0` state core, and Layout core.
## 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..1a89c7e 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 released registry fixtures; neither is a consumer dependency.
- 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..1c17fe2 100644
--- a/docs/wiki/Release-And-Publishing.md
+++ b/docs/wiki/Release-And-Publishing.md
@@ -1,12 +1,12 @@
# 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.
+- `CHANGELOG.md` contains the dated 2.1.0 release entry.
- README, migration guide, wiki, demo, exports, and tarball describe the same v2 API.
- No peer or runtime dependencies are present.
@@ -35,24 +35,26 @@ git diff --check
`release:verify` runs build, lint, JavaScript syntax, static contracts, the Pages artifact, Chromium, Firefox, WebKit, the intentional tarball listing, `npm audit --audit-level=moderate`, and an npm publish dry run. It does not publish.
+`prepublishOnly` runs `npm run release:verify`, so a direct `npm publish` still has the full release verification gate.
+
## 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..eeef7aa 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 released visual fixture for 2.1 ecosystem verification 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 released 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..67b828b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,18 +1,18 @@
{
"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": "2.1.0"
},
"engines": {
"node": ">=20"
@@ -848,9 +848,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,9 +1605,9 @@
}
},
"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==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/ui-style-kit-css/-/ui-style-kit-css-2.1.0.tgz",
+ "integrity": "sha512-GSMpvsxzr0Hr2rGDRGRxYM8y1mLEwt/q49OgCxik5EGH8L0bobe28wc1OYJ3xvOn0H9OtM2gKFLlkJZZL2zajw==",
"dev": true,
"license": "MIT",
"engines": {
diff --git a/package.json b/package.json
index 45a9e02..f982067 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",
@@ -47,16 +47,16 @@
"publish:dry-run": "npm publish --dry-run --access public --ignore-scripts",
"release:verify": "npm run check:full && npm audit --audit-level=moderate && npm run publish:dry-run",
"prepack": "npm run build && npm run test:static",
- "prepublishOnly": "npm run check:full"
+ "prepublishOnly": "npm run release:verify"
},
"publishConfig": {
"access": "public"
},
"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": "2.1.0"
},
"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..e5bbf23 100644
--- a/test/layout-css-contract.test.mjs
+++ b/test/layout-css-contract.test.mjs
@@ -602,10 +602,16 @@ 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 uiKitFixtureSpec = "2.1.0";
+const uiKitFixtureTarball =
+ "https://registry.npmjs.org/ui-style-kit-css/-/ui-style-kit-css-2.1.0.tgz";
+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 +619,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 +1211,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 +1226,27 @@ 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"], uiKitFixtureSpec);
+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"], uiKitFixtureSpec);
+assert.equal(lockRoot.devDependencies["interactive-surface-css"], "1.5.0");
+assert.equal(uiKitFixturePackage.version, "2.1.0", "UI Style Kit fixture must be the released 2.1.0 line");
+assert.equal(
+ packageLock.packages["node_modules/ui-style-kit-css"].resolved,
+ uiKitFixtureTarball,
+ "Lockfile must resolve the released UI Style Kit fixture from the npm registry"
+);
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, [
@@ -1293,7 +1305,7 @@ assert.equal(
"npm run check:full && npm audit --audit-level=moderate && npm run publish:dry-run"
);
assert.equal(packageJson.scripts.prepack, "npm run build && npm run test:static");
-assert.equal(packageJson.scripts.prepublishOnly, "npm run check:full");
+assert.equal(packageJson.scripts.prepublishOnly, "npm run release:verify");
assert.equal(packageJson.publishConfig.access, "public");
assert.deepEqual(packageJson.exports, expectedV2Exports);
for (const name of personalityNames) {
@@ -1305,6 +1317,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(/