Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/npm-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
28 changes: 16 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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:
Expand Down Expand Up @@ -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.

Expand All @@ -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`.

Expand Down Expand Up @@ -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 |
Expand All @@ -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 |
Expand All @@ -198,13 +200,13 @@ This order lets UI Style Kit establish paint and theme roles, Interactive Surfac
## CDN

```html
<link rel="stylesheet" href="https://unpkg.com/layout-style-css@3.1.0/dist/layout-style-css.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/layout-style-css@3.1.0/dist/layout-style-css.min.css">
<link rel="stylesheet" href="https://unpkg.com/layout-style-css@3.2.0/dist/layout-style-css.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/layout-style-css@3.2.0/dist/layout-style-css.min.css">
```

## 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

Expand All @@ -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)
Expand Down
73 changes: 72 additions & 1 deletion demo/demo.css
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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"] {
Expand Down
63 changes: 58 additions & 5 deletions demo/demo.js
Original file line number Diff line number Diff line change
@@ -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({
Expand All @@ -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",
Expand Down Expand Up @@ -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;
}
}
Expand All @@ -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)
: []
}));

Expand Down Expand Up @@ -114,7 +126,8 @@ function minimalPersonalityFallback() {
id: "minimal-saas",
label: "Minimal SaaS",
visualCompatibility: "any",
recommendedVisualPresets: []
recommendedVisualPresets: [],
compatibleVisualPresets: []
})
])
});
Expand Down Expand Up @@ -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");
Expand All @@ -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;
Expand Down Expand Up @@ -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];

Expand Down Expand Up @@ -715,6 +760,7 @@ function applyState(options = {}) {
syncHeightTier(allocation);

renderRecipe();
syncPairingGuidance();
syncUiKitClasses();
syncEcosystem();
syncSnippets();
Expand Down Expand Up @@ -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));
});
Expand Down
Loading
Loading