Interactive Surface is a framework-agnostic CSS interaction primitive for buttons, cards, icon controls, and similar click targets.
It provides consistent hover, focus-visible, active, press, and disabled behavior with token-driven theming, accessibility guardrails, and minimal integration friction.
Project docs live in this repository:
- Wiki Home
- Getting Started
- Installation and Usage
- API Reference
- Token Reference
- Accessibility
- Testing and Quality
- Publishing and Releases
- FAQ
- Roadmap
Community and governance docs:
- Package name:
interactive-surface-css - Primary stylesheet:
interactive-surface.css - JavaScript entry:
index.js(imports CSS for bundler-friendly usage) - Live demo:
https://foscat.github.io/Interactive-Surface-CSS/
Install:
npm install interactive-surface-cssImport:
import "interactive-surface-css";Or import CSS directly:
import "interactive-surface-css/interactive-surface.css";Note: The JavaScript entry imports CSS, so it should be used in bundlers or toolchains that support CSS imports. If you want the most portable, framework-agnostic path, import interactive-surface-css/interactive-surface.css directly. The package supports both approaches to accommodate different project setups and preferences.
Webpack:
-
Install loaders:
npm install -D css-loader style-loader
-
Configure
webpack.config.js:module.exports = { module: { rules: [ { test: /\.css$/i, use: ["style-loader", "css-loader"] } ] } };
-
Import in your app entry:
import "interactive-surface-css";
CDN:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/interactive-surface-css@latest/interactive-surface.css" />
<link rel="stylesheet" href="https://unpkg.com/interactive-surface-css@latest/interactive-surface.css" /><button class="interactive-surface">Save</button><button class="interactive-surface size-lg variant-primary">Continue</button><button class="interactive-surface icon-only" aria-label="Settings">
<svg aria-hidden="true" viewBox="0 0 24 24">...</svg>
</button>Live demo: Interactive Surface Demo
The demo page is a practical customization playground for this library:
- It provides guided token editing controls instead of freehand CSS typing.
- It supports importing and exporting token CSS so teams can reuse exact values.
- It helps reduce manual entry mistakes when creating app-level theme overrides.
Base:
.interactive-surface
Size modifiers:
.size-sm.size-lg- medium is default when no size class is set
State helpers:
.is-active.is-disabled
Semantic states:
[aria-pressed="true"][aria-disabled="true"]:disabled
Visual variants:
.variant-primary.variant-secondary.variant-accent.variant-subtle.variant-warning.variant-danger
Icon pattern:
.icon-only
Preferred token namespace:
--interactive-surface-*
The package also supports legacy fallback tokens and semantic fallback tokens. Full details and examples are in Token Reference.
Built-in support includes:
:focus-visiblebehavior with fallback handling- reduced-motion preference handling
- high-contrast and forced-colors handling
- ARIA pressed/disabled styling
- 44x44 minimum target size for
.icon-only
See Accessibility for implementation guidance.
npm run check:no-hex-colors
npm run lint:css
npm test
npm run test:chromium
npm run pack:dryThis repo is configured for release-driven npm publish through GitHub Actions at .github/workflows/npm-publish.yml.
Release checklist:
- Add repository secret
NPM_TOKEN(npm token with publish rights). - Bump
versioninpackage.json. - Update
CHANGELOG.md. - Push to
main. - Create and publish a GitHub Release tag (for example
v1.1.0). - Verify the
Publish to npmworkflow succeeds. - Verify CDN availability:
https://cdn.jsdelivr.net/npm/interactive-surface-css@<version>/interactive-surface.csshttps://unpkg.com/interactive-surface-css@<version>/interactive-surface.css
Manual fallback:
npm adduser
npm publish --access publicinteractive-surface should be the only transform-based motion owner on its host element.
Avoid applying additional transform, translate, scale, or rotate rules to the same node. If you need extra animation, apply it to a child element.