Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
06759e6
Add Interactive Surface data hook support
Jul 8, 2026
c2723fd
Fix cascade isolation, opacity resolution, and CDN version placeholder
Copilot Jul 8, 2026
8891dfa
Clarify version placeholder in CDN installation example
Copilot Jul 8, 2026
e3bf523
docs: design the 1.4.0 interaction refinement
Jul 19, 2026
b9d452f
docs: plan the 1.4.0 interaction refinement
Jul 19, 2026
541f09b
feat: add layered interaction stylesheet entry points
Jul 19, 2026
f190908
feat: harden accessible interaction state behavior
Jul 19, 2026
7e2508d
fix: preserve interaction state composition
Jul 19, 2026
7f5927a
fix: stabilize UI kit motion composition
Jul 19, 2026
0261f29
feat: turn the demo into an interaction state lab
Jul 19, 2026
71171c7
fix: complete state lab keyboard and modal feedback
Jul 19, 2026
ae03b3b
fix: reset dialog feedback between sessions
Jul 19, 2026
de22470
docs: publish the 1.4.0 interaction guide
Jul 19, 2026
67cc8dd
docs: lock package resolution guidance
Jul 19, 2026
5a3db31
chore: prepare the 1.4.0 release candidate
Jul 19, 2026
55679db
test: support Node 18 package verification
Jul 19, 2026
ba15641
test: allow native forced-color focus remapping
Jul 19, 2026
cca34f3
fix: validate clean release checkouts
Jul 19, 2026
976f9e7
test: isolate packed consumer resolution
Jul 19, 2026
0b016c5
test: make loading-state observation atomic
Jul 19, 2026
a039055
fix: keep the demo console clean
Jul 19, 2026
1360c5a
ci: harden release workflow dependencies
Jul 19, 2026
b93e26b
fix: make minimum-node CI executable
Jul 19, 2026
cb80882
[upgrade] Version 1.4.0
Jul 19, 2026
a12a363
Merge main into 1.4.0
Jul 19, 2026
4e215a4
Fix malformed table
Foscat Jul 19, 2026
98c919f
Incomplete multi-character sanitization
Foscat Jul 19, 2026
e890de4
Incomplete URL substring sanitization 1
Foscat Jul 19, 2026
4c1bf86
Incomplete URL substring sanitization 2
Foscat Jul 19, 2026
3da8068
Incomplete URL substring sanitization 3
Foscat Jul 19, 2026
0f2d224
Fix demo README fallback sync
Jul 19, 2026
efa26b9
feat: prepare interactive surface 1.5.0
Jul 20, 2026
2574a51
Merge main into 1.5.0
Jul 20, 2026
3940780
Fix file input motion contracts
Jul 20, 2026
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
35 changes: 35 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,41 @@ All notable changes to this package are documented in this file.

## Unreleased

## 1.5.0 - 2026-07-20

### Added

- Added public transition tuple tokens for interaction properties, duration, easing, and delay while retaining the established motion and easing fallback chains.
- Added rendered precedence coverage for combined hover, persistent, transient active, busy, loading, disabled, and focus-visible states.

### Changed

- Made disabled, busy/loading, transient active, persistent, hover, and base feedback follow one deterministic precedence order.
- Kept `:focus-visible` as an orthogonal outline so keyboard focus remains visible without replacing the active interaction state.
- Made the public hover lift token resolve directly, without hidden host-specific compensation.

### Fixed

- Removed the UI-host `+1px` hover adjustment that changed authored lift values when packages were combined.
- Removed the UI-specific transition override and cross-package `!important` repair from the standalone preset.
- Fixed persistent state feedback being replaced by hover and busy/loading feedback being replaced during a transient press.

### Accessibility

- Preserved a visible focus ring across base, pressed, selected, current, busy, and loading states.
- Kept disabled feedback authoritative while retaining reduced-motion state meaning and forced-colors system affordances.

### Documentation

- Documented the exact state precedence, transition tuple, legacy fallbacks, and optional UI Style Kit integration boundary across the README and wiki.
- Updated release-facing package, CDN, testing, publishing, and roadmap guidance for `1.5.0`.

### Testing

- Added static contracts for transition tokens, legacy entry points, and the absence of UI-specific offset and importance rules.
- Added exact computed-style assertions for transition tuples, transform composition, state precedence, focus-visible overlays, and native file selector behavior.
- Stabilized Playwright worker counts and exercised the package contract in Chromium, Firefox, and WebKit.

## 1.4.0 - 2026-07-19

### Added
Expand Down
26 changes: 15 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

Framework-agnostic CSS for reliable hover, focus, press, selected, current, loading, disabled, and motion affordances on interactive controls. Use it alone or as the interaction layer beside your existing layout and theme system.

Version 1.4.0 is a release candidate in this repository until its npm release is published. Existing 1.x imports, selectors, data hooks, ARIA hooks, and tokens remain supported.
Version 1.5.0 is a release candidate in this repository until its npm release is published. Existing 1.x imports, selectors, data hooks, ARIA hooks, and tokens remain supported.

The package targets Node.js 20+ for npm installs and local validation. CI proves the minimum Node 20 lane and the preferred Node 22 lane before release.

Expand Down Expand Up @@ -56,11 +56,11 @@ For a no-build page, pin the release:
```html
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/interactive-surface-css@1.4.0/standalone-preset.css"
href="https://cdn.jsdelivr.net/npm/interactive-surface-css@1.5.0/standalone-preset.css"
/>
```

The equivalent unpkg URL is `https://unpkg.com/interactive-surface-css@1.4.0/standalone-preset.css`. To follow future releases deliberately, use `https://cdn.jsdelivr.net/npm/interactive-surface-css@latest/standalone-preset.css` — unpinned opt-in.
The equivalent unpkg URL is `https://unpkg.com/interactive-surface-css@1.5.0/standalone-preset.css`. To follow future releases deliberately, use `https://cdn.jsdelivr.net/npm/interactive-surface-css@latest/standalone-preset.css` — unpinned opt-in.

## Semantic recipes

Expand Down Expand Up @@ -127,7 +127,7 @@ Native `disabled` is preferred because the browser suppresses focus and activati
| `import "interactive-surface-css/interactive-surface.css";` | Complete standalone compatibility bundle | Existing direct-CSS 1.x consumers |
| `import "interactive-surface-css";` | JavaScript entry that imports the complete compatibility bundle | Existing bundlers configured for CSS imports |

`standalone-preset.css` and `interactive-surface.css` are generated from the same authored modules and are behaviorally equivalent in 1.4.0. The compatibility paths remain stable; no 1.x migration is required.
`standalone-preset.css` and `interactive-surface.css` are generated from the same authored modules and are behaviorally equivalent in 1.5.0. The compatibility paths remain stable; no 1.x migration is required.

The package `main` and `module` fields preserve the CommonJS and ESM entries; both load `interactive-surface.css`. The `style`, `unpkg`, and `jsdelivr` fields also resolve to that complete compatibility bundle.

Expand All @@ -142,11 +142,11 @@ The package `main` and `module` fields preserve the CommonJS and ESM entries; bo
| Loading | `aria-busy="true"`, `.is-loading` |
| Disabled | native `:disabled`, `aria-disabled="true"`, `.is-disabled` |
| Variant | `.variant-primary`, `.variant-secondary`, `.variant-accent`, `.variant-subtle`, `.variant-warning`, `.variant-danger`, or matching `data-surface-variant` values |
| Level | `data-surface-level="1"`, `"2"`, or `"3"` |
| Icon | `.icon-only`; child `data-icon-role="light"`, `"dark"`, `"accessibility"`, or the legacy role classes |
| Level | `data-surface-level="1"`, `"2"`, or `"3"` |
| Icon | `.icon-only`; child `data-icon-role="light"`, `"dark"`, `"accessibility"`, or the legacy role classes |
| Native subcontrol | `input[type="file"].interactive-surface::file-selector-button` inherits surface paint and hover/active feedback |

Disabled state has highest visual precedence. The state layer preserves static meaning under reduced motion and uses system-color affordances in forced-colors mode. Interaction lift uses the individual `translate` property, so consumer-owned `transform`, `scale`, and `rotate` declarations can coexist.
Disabled > busy/loading > transient `:active` > pressed/selected/current > `:hover` > base. The `:focus-visible` ring is orthogonal to that precedence, so keyboard focus remains visible without replacing the active interaction state. The state layer preserves static meaning under reduced motion and uses system-color affordances in forced-colors mode. Interaction lift uses the individual `translate` property, so consumer-owned `transform`, `scale`, and `rotate` declarations can coexist.

For selector details and responsibilities, see the [complete API reference](https://github.com/Foscat/Interactive-Surface-CSS/wiki/API-Reference).

Expand All @@ -159,15 +159,19 @@ For selector details and responsibilities, see the [complete API reference](http
--interactive-surface-focus-ring-color: rgb(0 95 115);
--interactive-surface-state-layer-color: rgb(0 45 55);
--interactive-surface-state-layer-hover-opacity: 0.1;
--interactive-surface-motion-default: 120ms;
--interactive-surface-transition-property:
translate, box-shadow, outline-color;
--interactive-surface-transition-duration: 120ms;
--interactive-surface-transition-easing: cubic-bezier(0.2, 0, 0.2, 1);
--interactive-surface-transition-delay: 0ms;
}
```

All public custom properties use the `--interactive-surface-*` namespace. Established legacy and semantic fallback tokens remain supported. See the [token ownership tables and full reference](https://github.com/Foscat/Interactive-Surface-CSS/wiki/Token-Reference).
The transition defaults are `translate, box-shadow, outline-color`, the established default motion duration, the standard easing curve, and zero delay. Existing `--interactive-surface-motion-*`, `--interactive-surface-ease-*`, `--motion-*`, and `--ease-*` fallbacks remain supported. All public custom properties use the `--interactive-surface-*` namespace. See the [token ownership tables and full reference](https://github.com/Foscat/Interactive-Surface-CSS/wiki/Token-Reference).

## Accessibility responsibilities

The CSS package provides visible keyboard focus, persistent-state treatment, disabled precedence, reduced-motion behavior, higher-contrast behavior, forced-colors affordances, hover gating for capable pointers, and standalone icon target sizing.
The CSS package provides visible keyboard focus, persistent-state treatment, disabled precedence, reduced-motion behavior, higher-contrast behavior, forced-colors affordances, hover gating for capable pointers, and standalone icon target sizing. The `:focus-visible` ring remains orthogonal across every focusable non-disabled interaction state.

Applications still own semantics and behavior:

Expand All @@ -188,7 +192,7 @@ import "ui-style-kit-css/with-bridge.css";
import "interactive-surface-css/state-core.css";
```

The bridge maps active UI Style Kit theme and mode values into the `--interactive-surface-*` contract. Interactive Surface keeps ownership of focus, hover, pressed, selected, current, loading, disabled, and motion behavior.
The bridge maps active UI Style Kit theme and mode values into the `--interactive-surface-*` contract. Interactive Surface keeps ownership of focus, hover, pressed, selected, current, loading, disabled, and motion behavior. UI Style Kit remains optional: this package declares no runtime or peer dependency on it, and the documented import order keeps its paint layer before `state-core.css`.

## Use all three libraries

Expand Down
63 changes: 46 additions & 17 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,35 @@
/>
<meta name="color-scheme" content="light dark" />
<link rel="icon" href="./assets/favicon.ico" sizes="any" />
<link rel="icon" type="image/png" sizes="16x16" href="./assets/favicon-16x16.png" />
<link rel="icon" type="image/png" sizes="32x32" href="./assets/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="48x48" href="./assets/favicon-48x48.png" />
<link rel="icon" type="image/png" sizes="64x64" href="./assets/favicon-64x64.png" />
<link rel="apple-touch-icon" sizes="180x180" href="./assets/apple-touch-icon.png" />
<link
rel="icon"
type="image/png"
sizes="16x16"
href="./assets/favicon-16x16.png"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="./assets/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="48x48"
href="./assets/favicon-48x48.png"
/>
<link
rel="icon"
type="image/png"
sizes="64x64"
href="./assets/favicon-64x64.png"
/>
<link
rel="apple-touch-icon"
sizes="180x180"
href="./assets/apple-touch-icon.png"
/>
<link rel="manifest" href="./assets/site.webmanifest" />
<meta name="msapplication-TileColor" content="#09131c" />
<meta name="msapplication-config" content="./assets/browserconfig.xml" />
Expand Down Expand Up @@ -538,6 +562,7 @@ <h2 id="tokenEditorTitle">Edit token</h2>
</form>
</dialog>

<!-- prettier-ignore -->
<script id="embeddedReadme" type="text/markdown">
# Interactive Surface CSS

Expand All @@ -546,7 +571,7 @@ <h2 id="tokenEditorTitle">Edit token</h2>

Framework-agnostic CSS for reliable hover, focus, press, selected, current, loading, disabled, and motion affordances on interactive controls. Use it alone or as the interaction layer beside your existing layout and theme system.

Version 1.4.0 is a release candidate in this repository until its npm release is published. Existing 1.x imports, selectors, data hooks, ARIA hooks, and tokens remain supported.
Version 1.5.0 is a release candidate in this repository until its npm release is published. Existing 1.x imports, selectors, data hooks, ARIA hooks, and tokens remain supported.

The package targets Node.js 20+ for npm installs and local validation. CI proves the minimum Node 20 lane and the preferred Node 22 lane before release.

Expand Down Expand Up @@ -597,11 +622,11 @@ <h2 id="tokenEditorTitle">Edit token</h2>
```html
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/interactive-surface-css@1.4.0/standalone-preset.css"
href="https://cdn.jsdelivr.net/npm/interactive-surface-css@1.5.0/standalone-preset.css"
/>
```

The equivalent unpkg URL is `https://unpkg.com/interactive-surface-css@1.4.0/standalone-preset.css`. To follow future releases deliberately, use `https://cdn.jsdelivr.net/npm/interactive-surface-css@latest/standalone-preset.css` — unpinned opt-in.
The equivalent unpkg URL is `https://unpkg.com/interactive-surface-css@1.5.0/standalone-preset.css`. To follow future releases deliberately, use `https://cdn.jsdelivr.net/npm/interactive-surface-css@latest/standalone-preset.css` — unpinned opt-in.

## Semantic recipes

Expand Down Expand Up @@ -668,7 +693,7 @@ <h2 id="tokenEditorTitle">Edit token</h2>
| `import "interactive-surface-css/interactive-surface.css";` | Complete standalone compatibility bundle | Existing direct-CSS 1.x consumers |
| `import "interactive-surface-css";` | JavaScript entry that imports the complete compatibility bundle | Existing bundlers configured for CSS imports |

`standalone-preset.css` and `interactive-surface.css` are generated from the same authored modules and are behaviorally equivalent in 1.4.0. The compatibility paths remain stable; no 1.x migration is required.
`standalone-preset.css` and `interactive-surface.css` are generated from the same authored modules and are behaviorally equivalent in 1.5.0. The compatibility paths remain stable; no 1.x migration is required.

The package `main` and `module` fields preserve the CommonJS and ESM entries; both load `interactive-surface.css`. The `style`, `unpkg`, and `jsdelivr` fields also resolve to that complete compatibility bundle.

Expand All @@ -683,11 +708,11 @@ <h2 id="tokenEditorTitle">Edit token</h2>
| Loading | `aria-busy="true"`, `.is-loading` |
| Disabled | native `:disabled`, `aria-disabled="true"`, `.is-disabled` |
| Variant | `.variant-primary`, `.variant-secondary`, `.variant-accent`, `.variant-subtle`, `.variant-warning`, `.variant-danger`, or matching `data-surface-variant` values |
| Level | `data-surface-level="1"`, `"2"`, or `"3"` |
| Icon | `.icon-only`; child `data-icon-role="light"`, `"dark"`, `"accessibility"`, or the legacy role classes |
| Level | `data-surface-level="1"`, `"2"`, or `"3"` |
| Icon | `.icon-only`; child `data-icon-role="light"`, `"dark"`, `"accessibility"`, or the legacy role classes |
| Native subcontrol | `input[type="file"].interactive-surface::file-selector-button` inherits surface paint and hover/active feedback |

Disabled state has highest visual precedence. The state layer preserves static meaning under reduced motion and uses system-color affordances in forced-colors mode. Interaction lift uses the individual `translate` property, so consumer-owned `transform`, `scale`, and `rotate` declarations can coexist.
Disabled > busy/loading > transient `:active` > pressed/selected/current > `:hover` > base. The `:focus-visible` ring is orthogonal to that precedence, so keyboard focus remains visible without replacing the active interaction state. The state layer preserves static meaning under reduced motion and uses system-color affordances in forced-colors mode. Interaction lift uses the individual `translate` property, so consumer-owned `transform`, `scale`, and `rotate` declarations can coexist.

For selector details and responsibilities, see the [complete API reference](https://github.com/Foscat/Interactive-Surface-CSS/wiki/API-Reference).

Expand All @@ -700,15 +725,19 @@ <h2 id="tokenEditorTitle">Edit token</h2>
--interactive-surface-focus-ring-color: rgb(0 95 115);
--interactive-surface-state-layer-color: rgb(0 45 55);
--interactive-surface-state-layer-hover-opacity: 0.1;
--interactive-surface-motion-default: 120ms;
--interactive-surface-transition-property:
translate, box-shadow, outline-color;
--interactive-surface-transition-duration: 120ms;
--interactive-surface-transition-easing: cubic-bezier(0.2, 0, 0.2, 1);
--interactive-surface-transition-delay: 0ms;
}
```

All public custom properties use the `--interactive-surface-*` namespace. Established legacy and semantic fallback tokens remain supported. See the [token ownership tables and full reference](https://github.com/Foscat/Interactive-Surface-CSS/wiki/Token-Reference).
The transition defaults are `translate, box-shadow, outline-color`, the established default motion duration, the standard easing curve, and zero delay. Existing `--interactive-surface-motion-*`, `--interactive-surface-ease-*`, `--motion-*`, and `--ease-*` fallbacks remain supported. All public custom properties use the `--interactive-surface-*` namespace. See the [token ownership tables and full reference](https://github.com/Foscat/Interactive-Surface-CSS/wiki/Token-Reference).

## Accessibility responsibilities

The CSS package provides visible keyboard focus, persistent-state treatment, disabled precedence, reduced-motion behavior, higher-contrast behavior, forced-colors affordances, hover gating for capable pointers, and standalone icon target sizing.
The CSS package provides visible keyboard focus, persistent-state treatment, disabled precedence, reduced-motion behavior, higher-contrast behavior, forced-colors affordances, hover gating for capable pointers, and standalone icon target sizing. The `:focus-visible` ring remains orthogonal across every focusable non-disabled interaction state.

Applications still own semantics and behavior:

Expand All @@ -729,7 +758,7 @@ <h2 id="tokenEditorTitle">Edit token</h2>
import "interactive-surface-css/state-core.css";
```

The bridge maps active UI Style Kit theme and mode values into the `--interactive-surface-*` contract. Interactive Surface keeps ownership of focus, hover, pressed, selected, current, loading, disabled, and motion behavior.
The bridge maps active UI Style Kit theme and mode values into the `--interactive-surface-*` contract. Interactive Surface keeps ownership of focus, hover, pressed, selected, current, loading, disabled, and motion behavior. UI Style Kit remains optional: this package declares no runtime or peer dependency on it, and the documented import order keeps its paint layer before `state-core.css`.

## Use all three libraries

Expand All @@ -753,7 +782,7 @@ <h2 id="tokenEditorTitle">Edit token</h2>
- [Code of Conduct](https://github.com/Foscat/Interactive-Surface-CSS/blob/main/CODE_OF_CONDUCT.md)
- [Security policy](https://github.com/Foscat/Interactive-Surface-CSS/blob/main/SECURITY.md)
- [MIT License](https://github.com/Foscat/Interactive-Surface-CSS/blob/main/LICENSE)
</script>
</script>
<script src="./demo/demo.js" defer></script>
</body>
</html>
Loading