diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index 7846905..e5b341e 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 v3.1.0" + description: "Release tag to publish, for example v3.2.0" required: true type: string diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b47cde..3d57df1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,28 @@ All notable changes to `layout-style-css` are documented here. This project follows semantic versioning. +## [3.2.0] - 2026-09-08 + +### Added + +- Added Mosaic and Action Bar compositions for responsive dashboards, task bars, and workflow surfaces that need stable DOM order and wrapping actions. +- Added four specialized layout personalities: Technical Blueprint, Data Terminal, Industrial HMI, and Editorial. +- Added the Layout Personality Reference and the 3.2 migration guide covering Mosaic, Action Bar, area-aware App Shell behavior, Scroll, and content resilience. + +### Changed + +- Made App Shell area-aware so absent direct-child side regions do not reserve empty tracks while complete shells preserve their approved topology. +- Hardened layout content tracks and Scroll behavior so long labels, technical content, tables, and all-three-library fixtures remain usable with UI Style Kit 2.4.0. +- Expanded personality pairing metadata with recommended and compatible visual presets for the complete 20-layout inventory. + +### Security + +- Updated the audited `fast-uri` override to the fixed 3.1.7 line while keeping runtime and peer dependencies empty. + +### Tests + +- Added focused and rendered coverage for Mosaic, Action Bar, area-aware shells, pairing metadata, generated artifacts, release documentation, Pages output, and the final UI Style Kit 2.4.0 ecosystem fixture. + ## [3.1.0] - 2026-08-25 ### Added diff --git a/README.md b/README.md index cc3f4b7..9277f0f 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Zero-configuration, dependency-free structural CSS that responds to the width and height a layout actually receives. -`layout-style-css@3.1.0` is the current additive v3 release. It provides intrinsic wrappers, composition primitives, seven semantic recipes, explicit local density controls, and sixteen spatial personalities. Layout owns structure; UI Style Kit owns paint; Interactive Surface owns interaction styling. +`layout-style-css@3.2.0` is the current v3 release candidate. It provides intrinsic wrappers, Mosaic and Action Bar compositions, seven semantic recipes, explicit local density controls, and twenty spatial personalities. Layout owns structure; UI Style Kit owns paint; Interactive Surface owns interaction styling. ## Requirements @@ -13,7 +13,7 @@ Zero-configuration, dependency-free structural CSS that responds to the width an ## Install ```bash -npm install layout-style-css@3.1.0 +npm install layout-style-css@3.2.0 ``` Most applications need one import: @@ -87,11 +87,11 @@ All lanes clamp to the available inline size, including allocations from 320px t The core includes: - `.ly-stack`, `.ly-cluster`, `.ly-center`, and `.ly-cover` -- `.ly-switcher`, `.ly-sidebar`, `.ly-grid`, and `.ly-split` +- `.ly-switcher`, `.ly-sidebar`, `.ly-grid`, `.ly-mosaic`, and `.ly-split` - `.ly-panes` and `.ly-media` -- `.ly-reel`, `.ly-frame`, and `.ly-scroll` +- `.ly-action-bar`, `.ly-reel`, `.ly-frame`, and `.ly-scroll` -Grid and flex primitives wrap intrinsically whenever track wrapping can replace a query. Base `.ly-scroll` owns overflow but no height cap. Add `.ly-scroll--bounded` for the stable `--ly-scroll-max` cap or `.ly-scroll--viewport` for height-aware `--ly-scroll-viewport-max` behavior. Only `.ly-reel` deliberately scrolls horizontally; only the Scroll family deliberately creates vertical scrolling. +Grid and flex primitives wrap intrinsically whenever track wrapping can replace a query. Mosaic uses one, six, and twelve tracks at its base, `42rem`, and `72rem` tiers while keeping DOM order authoritative. Base `.ly-scroll` owns horizontal and vertical overflow but no height cap. Add `.ly-scroll--bounded` for the stable `--ly-scroll-max` cap or `.ly-scroll--viewport` for height-aware `--ly-scroll-viewport-max` behavior. Gap utilities `.ly-gap-0` through `.ly-gap-9` assign `gap` only on the element carrying the class. They do not alter inherited primitive gap variables. Override `--ly-gap`, `--ly-grid-gap`, `--ly-stack-gap`, or `--ly-cluster-gap` when a shared spacing context is intentional. @@ -111,7 +111,7 @@ Recipes use attributes only. The stacked semantic source order is always safe, a The same ownership is available in `manifest.json` through `thresholds.recipes`, alongside the generic container-width and viewport-height arrays. Release checks verify every mapping against authored queries and require every public geometry token to reach a runtime CSS declaration. -App Shell base, medium, and wide topologies own matching row definitions. This keeps the primary workspace flexible while header and footer tracks remain intrinsic, including personality-specific four-row topologies. +App Shell base, medium, and wide topologies own matching row definitions. The automatic shell is area-aware: absent direct-child side regions do not reserve empty tracks, while full shells preserve personality-approved geometry. `--ly-recipe-main-min: 20rem` guards automatic primary tracks. Canonical regions use `data-ly-area`, including `header`, `sidebar`, `nav`, `main`, `aside`, `footer`, `content`, `media`, `actions`, `primary`, and `secondary`. @@ -155,12 +155,14 @@ Use the public height, gap, measure, ratio, rail, media, card, and grid-minimum ## Personalities -Set one of sixteen canonical `data-ly-layout` values on `.ly-root`: +Set one of twenty canonical `data-ly-layout` values on `.ly-root`: -`minimal-saas`, `bauhaus`, `tactile`, `cyberpunk`, `f-pattern`, `brutalism`, `neumorphism`, `y2k`, `retro-glass`, `z-pattern`, `retrofuturism`, `mondrian`, `synthwave`, `bento`, `maximalist`, or `split-screen`. +`minimal-saas`, `bento`, `maximalist`, `bauhaus`, `tactile`, `neumorphism`, `retrofuturism`, `brutalism`, `cyberpunk`, `y2k`, `retro-glass`, `f-pattern`, `z-pattern`, `split-screen`, `mondrian`, `synthwave`, `technical-blueprint`, `data-terminal`, `industrial-hmi`, or `editorial`. Each personality is a token/topology profile consumed by the shared recipe engine. A profile changes at least two spatial characteristics—such as measure, gap, rail, media size, card minimum, or ratio—but does not declare its own container or viewport breakpoint system. +See the [Layout Personality Reference](docs/wiki/Layout-Personality-Reference.md) for canonical geometry, responsive intent, Mosaic usage, and recommended/compatible UI presets. + ## Public Exports | Import | Contents | @@ -173,7 +175,7 @@ Each personality is a token/topology profile consumed by the shared recipe engin | `layout-style-css/primitives.css` | Intrinsic composition primitives | | `layout-style-css/recipes.css` | Seven attribute-only recipes | | `layout-style-css/utilities.css` | Small structural utility set | -| `layout-style-css/personalities.css` | All sixteen profiles | +| `layout-style-css/personalities.css` | All twenty profiles | | `layout-style-css/personalities/minimal-saas.css` | One profile; substitute any public personality name | | `layout-style-css/personalities.json` | Public layout-to-visual pairing recommendations | | `layout-style-css/package.json` | Package metadata | @@ -198,13 +200,13 @@ This order lets UI Style Kit establish paint and theme roles, Interactive Surfac ## CDN ```html - - + + ``` ## Clean-Break Migration -v3 has no compatibility bundle. It removes `legacy.css`, the deprecated UI Style Kit structural bridge, v1/v2 aliases, responsive `ly-md-*` and `ly-lg-*` utilities, and all visual-order utilities. See [Migrating To 3.0](docs/wiki/Migrating-To-3.0.md) for the major-version mappings and [Migrating To 3.1](docs/wiki/Migrating-To-3.1.md) for density, local gap, and Scroll behavior changes. +v3 has no compatibility bundle. It removes `legacy.css`, the deprecated UI Style Kit structural bridge, v1/v2 aliases, responsive `ly-md-*` and `ly-lg-*` utilities, and all visual-order utilities. See [Migrating To 3.0](docs/wiki/Migrating-To-3.0.md) for the major-version mappings, [Migrating To 3.1](docs/wiki/Migrating-To-3.1.md) for density and local-gap changes, and [Migrating To 3.2](docs/wiki/Migrating-To-3.2.md) for Mosaic, Action Bar, area-aware shells, and content resilience. ## Demo And Verification @@ -229,8 +231,10 @@ npm run release:verify - [Layout Primitives](docs/wiki/Layout-Primitives.md) - [Layout Recipes](docs/wiki/Layout-Recipes.md) - [Layout Styles](docs/wiki/Layout-Styles.md) +- [Layout Personality Reference](docs/wiki/Layout-Personality-Reference.md) - [Migrating To 3.0](docs/wiki/Migrating-To-3.0.md) - [Migrating To 3.1](docs/wiki/Migrating-To-3.1.md) +- [Migrating To 3.2](docs/wiki/Migrating-To-3.2.md) - [Demo And GitHub Pages](docs/wiki/Demo-And-GitHub-Pages.md) - [Release And Publishing](docs/wiki/Release-And-Publishing.md) - [Security And Support](docs/wiki/Security-And-Support.md) diff --git a/demo/demo.css b/demo/demo.css index c6a753f..66f171a 100644 --- a/demo/demo.css +++ b/demo/demo.css @@ -201,6 +201,17 @@ h3 { font-size: 0.875rem; } +.demo-pairing-guidance { + display: grid; + gap: 0.25rem; + padding: 0.75rem; + border: 1px solid var(--demo-line); + border-radius: calc(var(--demo-radius) * 0.6); + background: var(--demo-panel-strong); + color: var(--demo-muted); + font-size: 0.75rem; +} + .demo-readouts { display: grid; justify-items: end; @@ -389,12 +400,72 @@ body[data-ecosystem="all-three"] .demo-status { min-inline-size: 0; gap: 0.75rem; padding: var(--demo-region-padding); - overflow: hidden; border: 1px solid var(--demo-line); border-radius: calc(var(--demo-radius) * 0.65); background: var(--demo-panel); } +.demo-composition-fixtures { + display: grid; + min-inline-size: 0; + gap: var(--demo-density-gap); + margin-block-start: var(--demo-density-gap); +} + +.demo-fixture { + display: grid; + min-inline-size: 0; + gap: 0.75rem; + padding: var(--demo-region-padding); + border: 1px solid var(--demo-line); + border-radius: calc(var(--demo-radius) * 0.65); + background: var(--demo-panel); +} + +.demo-fixture-heading { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + gap: 0.5rem; + color: var(--demo-muted); + font-size: 0.8125rem; +} + +.demo-mosaic { + min-inline-size: 0; +} + +.demo-mosaic-item { + min-block-size: 4rem; + min-inline-size: 0; + padding: 0.75rem; + border: 1px solid var(--demo-line); + border-radius: calc(var(--demo-radius) * 0.45); + background: var(--demo-panel-strong); +} + +.demo-resilience-scroll { + --ly-scroll-max: 8rem; +} + +.demo-resilience-scroll table { + min-inline-size: 50rem; + border-collapse: collapse; +} + +.demo-resilience-scroll th, +.demo-resilience-scroll td { + padding: 0.5rem 0.75rem; + border: 1px solid var(--demo-line); + text-align: start; + white-space: nowrap; +} + +.demo-resilience-scroll th { + background: var(--demo-panel-strong); +} + .demo-region[data-ly-area="main"], .demo-region[data-ly-area="secondary"], .demo-region[data-ly-area="content"] { diff --git a/demo/demo.js b/demo/demo.js index ca80265..7ddc821 100644 --- a/demo/demo.js +++ b/demo/demo.js @@ -1,4 +1,4 @@ -const UI_STYLE_KIT_VERSION = "2.1.0"; +const UI_STYLE_KIT_VERSION = "2.4.0"; const UI_STYLE_KIT_MANIFEST_URL = `https://unpkg.com/ui-style-kit-css@${UI_STYLE_KIT_VERSION}/manifest.json`; const PERSONALITY_METADATA_URL = document.querySelector("script[data-personalities-url]")?.dataset.personalitiesUrl; const UI_STYLE_KIT_MANIFEST_FALLBACK = Object.freeze({ @@ -14,7 +14,16 @@ const UI_STYLE_KIT_MANIFEST_FALLBACK = Object.freeze({ 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" }) + Object.freeze({ id: "retro-glass", label: "Retro Glass", prefix: "rg" }), + Object.freeze({ id: "editorial-luxe", label: "Editorial Luxe", prefix: "luxe" }), + Object.freeze({ id: "organic-modern", label: "Organic Modern", prefix: "organic" }), + Object.freeze({ id: "industrial-utility", label: "Industrial Utility", prefix: "utility" }), + Object.freeze({ id: "technical-blueprint", label: "Technical Blueprint", prefix: "blueprint" }), + Object.freeze({ id: "art-deco", label: "Art Deco", prefix: "deco" }), + Object.freeze({ id: "clay", label: "Clay", prefix: "clay" }), + Object.freeze({ id: "data-terminal", label: "Data Terminal", prefix: "terminal" }), + Object.freeze({ id: "paper-editorial", label: "Paper Editorial", prefix: "paper" }), + Object.freeze({ id: "neo-noir", label: "Neo Noir", prefix: "noir" }) ]), themes: Object.freeze([ "midnight-gold", @@ -70,9 +79,9 @@ async function loadUiStyleKitManifest() { } 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. + The fallback mirrors the 2.3 manifest contract so layout behavior stays testable. */ - console.warn("UI Style Kit 2.1 manifest unavailable; using the packaged fallback.", error); + console.warn("UI Style Kit 2.3 manifest unavailable; using the packaged fallback.", error); return UI_STYLE_KIT_MANIFEST_FALLBACK; } } @@ -87,6 +96,9 @@ function normalizePersonalityMetadata(metadata) { visualCompatibility: String(personality?.visualCompatibility ?? ""), recommendedVisualPresets: Array.isArray(personality?.recommendedVisualPresets) ? personality.recommendedVisualPresets.map(String) + : [], + compatibleVisualPresets: Array.isArray(personality?.compatibleVisualPresets) + ? personality.compatibleVisualPresets.map(String) : [] })); @@ -114,7 +126,8 @@ function minimalPersonalityFallback() { id: "minimal-saas", label: "Minimal SaaS", visualCompatibility: "any", - recommendedVisualPresets: [] + recommendedVisualPresets: [], + compatibleVisualPresets: [] }) ]) }); @@ -301,6 +314,8 @@ const importsSnippet = document.querySelector("#importsSnippet"); const markupSnippet = document.querySelector("#markupSnippet"); const copyStatus = document.querySelector("#copyStatus"); const personalityMetadataStatus = document.querySelector("#personalityMetadataStatus"); +const recommendedUiGuidance = document.querySelector("#recommendedUiGuidance"); +const compatibleUiGuidance = document.querySelector("#compatibleUiGuidance"); const ecosystemStatus = document.querySelector("#ecosystemStatus"); const containerReadout = document.querySelector("#containerReadout"); const topologyReadout = document.querySelector("#topologyReadout"); @@ -313,6 +328,8 @@ const drawer = document.querySelector("#demoControlsDrawer"); const drawerClose = document.querySelector("#demoControlsClose"); const drawerBackdrop = document.querySelector("#demoControlsBackdrop"); const stateToggle = document.querySelector("#stateToggle"); +const actionBarStickyToggle = document.querySelector("#actionBarStickyToggle"); +const fixtureActionBar = document.querySelector("#fixtureActionBar"); const mobileControlsQuery = window.matchMedia("(max-width: 63.999rem)"); body.dataset.uiManifestVersion = UI_STYLE_KIT_MANIFEST.version; @@ -546,6 +563,34 @@ function syncPersonalityMetadataSelectOptions() { select.setAttribute("aria-busy", "false"); } +/** + * Renders advisory visual-preset pairings for the selected layout without + * mutating the independently selected UI preset. + * + * @returns {void} + */ +function syncPairingGuidance() { + const pairing = PERSONALITY_METADATA.personalities.find(({ id }) => id === state.personality); + /** + * Formats visual-preset identifiers for human-readable guidance. + * + * @param {string[]} presets Visual-preset identifiers. + * @param {string} fallback Text shown when no presets are listed. + * @returns {string} Display-ready visual-preset guidance. + */ + const describePresets = (presets, fallback) => + presets.length > 0 ? presets.map(formatLabel).join(", ") : fallback; + + recommendedUiGuidance.textContent = `Recommended UI: ${describePresets( + pairing?.recommendedVisualPresets ?? [], + pairing?.visualCompatibility === "any" ? "Any visual preset" : "None listed" + )}`; + compatibleUiGuidance.textContent = `Compatible UI: ${describePresets( + pairing?.compatibleVisualPresets ?? [], + "None listed" + )}`; +} + function syncUiKitClasses() { const prefix = UI_CLASS_PREFIXES[state.ui]; @@ -715,6 +760,7 @@ function applyState(options = {}) { syncHeightTier(allocation); renderRecipe(); + syncPairingGuidance(); syncUiKitClasses(); syncEcosystem(); syncSnippets(); @@ -852,6 +898,13 @@ stateToggle.addEventListener("click", () => { stateToggle.textContent = pressed ? "Active state on" : "Toggle active state"; }); +actionBarStickyToggle.addEventListener("click", () => { + const pressed = actionBarStickyToggle.getAttribute("aria-pressed") !== "true"; + actionBarStickyToggle.setAttribute("aria-pressed", String(pressed)); + actionBarStickyToggle.textContent = pressed ? "Disable sticky mode" : "Enable sticky mode"; + fixtureActionBar.classList.toggle("ly-action-bar--sticky", pressed); +}); + document.querySelectorAll("[data-copy-target]").forEach((button) => { button.addEventListener("click", () => copySnippet(button)); }); diff --git a/demo/index.html b/demo/index.html index 9518cab..613b3a2 100644 --- a/demo/index.html +++ b/demo/index.html @@ -5,9 +5,9 @@ - - - + + + @@ -37,8 +37,8 @@ - - + + @@ -47,13 +47,13 @@ - + - + - + @@ -66,40 +66,40 @@ - Layout Style CSS v3 — Intrinsic Responsive Demo + Layout Style CSS v3.2 — Intrinsic Responsive Demo - - + + - - + + - - + + Skip to the layout lab
- Layout Style CSS v3 + Layout Style CSS v3.2 Intrinsic Responsive Lab
@@ -168,6 +168,10 @@

Layout controls

+
+

Recommended UI: Minimal SaaS

+

Compatible UI: Organic Modern

+