npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@trap_stevo/axis

v0.0.2

Published

Universal interface intelligence engine unifying real environment signals into a single immutable snapshot for explicit, predictable layout decisions across every screen.

Readme

🧭 @trap_stevo/axis

Universal Interface Intelligence Engine
Every device. Every shape. One layout mind.

A deterministic, framework-agnostic interface intelligence engine,
unifying capability detection, viewport reality, layout scale, aspect awareness, safe-area constraints, ergonomic rules, grid systems, typography signals, UI runtime helpers, profile groups, and resolver-driven values into a single immutable snapshot — enabling explicit, predictable, and reusable layout decisions across every screen and input context.

Axis does not render UI.
Axis does not replace CSS.
Axis supplies facts so layout logic stays explicit, predictable, and reusable.


Why Axis Exists

Interfaces fail for repeatable reasons:

  • breakpoint sprawl
  • duplicated layouts across form factors
  • touch vs pointer conflicts
  • hybrid laptops and edge devices
  • gradual layout drift

Viewport width alone cannot describe interaction.
Device detection collapses under hybrids.

Axis measures reality, derives intent-ready signals, and normalizes outcomes.


What Axis Covers — and What It Leaves Alone

Axis covers

  • interface intelligence
  • deterministic snapshots
  • capability-driven decisions
  • continuous scaling
  • ergonomic normalization
  • safe-area and viewport reality
  • profile identities and profile groups
  • UI runtime helpers derived from the snapshot
  • resolver-driven values for app-specific configuration
  • framework-agnostic consumption

Axis leaves alone

  • rendering
  • CSS authoring
  • design systems
  • component libraries

Axis answers what the environment looks like.
Your code answers what to do about it.


✨ Feature Overview

🧠 Capabilities

  • touch / pointer / hybrid detection
  • hover and coarse/fine pointer awareness
  • touch-laptop recognition
  • max touch points signal

📐 Space & Aspect

  • orientation awareness
  • aspect ratio classification
  • shape detection: tall / standard / wide / ultraWide / squareish

🪜 Scaling

  • continuous scale factor
  • rem / px conversion (raw + CSS-ready)
  • spacing, radius, shadow, icon, control-height, and width helpers
  • explicit conversions: px→rem and rem→px

🧩 Layout

  • stack vs split layout mode
  • container math
  • grid math
  • nested layouts

🎯 Ergonomics

  • minimum hit targets
  • control height normalization
  • hybrid input handling
  • tier normalization (pointer compact windows normalize to comfortable)

🔤 Typography

  • fluid type ramp
  • line-height presets
  • readable line-length helpers
  • clamp helpers

🧷 Reality Handling

  • safe-area insets (CSS + resolved px)
  • visual viewport tracking
  • mobile keyboard heuristics
  • viewport unit resolution (svh/dvh/lvh)

🧭 Identity & Grouping

  • canonical profile IDs
  • group-based matching (mobileLike, desktopLike, keyboardOpen, etc.)
  • profile-based matching and declarative branching

🧩 UI Runtime Helpers

  • UI scale mode (auto/compact/comfortable/large)
  • UI rem/px helpers
  • UI spacing, radius, hit target helpers

🧷 Resolver Values

  • optional resolver-driven values via resolve() and resolveString()

🔄 Integration

  • reactive subscriptions
  • RAF-coalesced updates
  • SSR-safe fallback snapshot

🖥️ System Requirements

| Category | Requirement | |--------|-------------| | Runtime | ES2020+, ESM | | Environment | Browser or Node.js | | Browser Support | Chrome / Edge (latest), Firefox (latest), Safari 16+, iOS Safari 16+ | | SSR Support | Yes — stable fallback snapshot when window remains unavailable | | Input Types | Touch, pointer, hybrid | | Viewport APIs | Uses visualViewport when available; graceful fallback otherwise | | Safe Area APIs | Uses CSS env(safe-area-inset-*) when supported | | Device Coverage | Phones, tablets, desktops, hybrid laptops, ultra-wide displays | | Dependencies | None |


The Mental Model

Everything flows from a snapshot.

  • snapshot creation follows environmental change
  • snapshot contents describe the full interface environment
  • snapshot immutability prevents drift
  • one subscription supports unlimited reuse

Query once.
Reuse everywhere.


Core Signals

Orientation

axis.orientation; // "portrait" | "landscape"

Input

axis.input; // "touch" | "pointer" | "hybrid"

Tier

axis.tier;    // "compact" | "comfortable" | "expanded" | "cinema"
axis.tierRaw; // raw tier before normalization

Aspect & Shape

axis.aspect.ratio;
axis.aspect.shape;

Profile Identity

axis.profile.id; // TouchCompactTall

📘 Axis Type Reference

AxisEngine

| Member | Type | Description | |---|---|---| | snapshot() | () => AxisSnapshot | Returns current immutable snapshot | | subscribe(handler) | (axis:AxisSnapshot) => () => void | Subscribes to snapshot updates | | setProfileOverride(id) | (AxisProfileID | null) => void | Forces a profile identity | | getProfileOverride() | () => AxisProfileID | null | Reads override state | | setResolver(source) | (AxisResolveSource | null) => void | Installs a resolver source | | getResolver() | () => AxisResolveSource | null | Reads resolver source | | destroy() | () => void | Removes listeners and subscriptions |


AxisSnapshot

| Field | Type | Description | |---|---|---| | caps | AxisCaps | Capability signals | | orientation | "portrait" | "landscape" | Screen orientation | | viewportWidth | number | Layout viewport width | | viewportHeight | number | Layout viewport height | | shortSide | number | Shortest viewport edge | | input | "touch" | "pointer" | "hybrid" | Derived input mode | | tierRaw | "compact" | "comfortable" | "expanded" | "cinema" | Raw tier (pre-normalization) | | tier | "compact" | "comfortable" | "expanded" | "cinema" | Normalized tier | | aspect | AxisAspect | Ratio + shape helpers | | layoutMode | "stack" | "split" | Primary layout mode | | profile | AxisProfile | Canonical identity | | scale | AxisScale | Measurement helpers | | viewport | AxisViewport | Layout + visual viewport reality | | safeArea | AxisSafeArea | Safe-area insets | | insets | AxisInsets | Content padding and gutters | | controls | AxisControls | Ergonomic sizing helpers | | density | AxisDensity | Density factor + override | | container | AxisContainer | Container sizing helpers | | grid | AxisGrid | Grid helpers | | type | AxisType | Typography helpers | | ui | AxisUI | UI runtime helpers derived from the snapshot | | resolve(key) | (key:string) => AxisResolveValue | Resolver-driven value | | resolveString(key,fallback) | (key:string,fallback:string)=>string | Resolver string helper | | isProfile(profiles) | (AxisProfileID | AxisProfileID[]) => boolean | Profile identity check | | only(profiles,fn) | (AxisProfileID | AxisProfileID[], fn:()=>void)=>void | Execute for matching profiles | | unless(profiles,fn) | (AxisProfileID | AxisProfileID[], fn:()=>void)=>void | Execute for non-matching profiles | | onlyIf(predicate,fn) | (predicate:(axis)=>boolean, fn:()=>void)=>void | Predicate-based execution | | match(cases,fallback) | (Array<[AxisProfileID|AxisProfileID[],T]>,T)=>T | Declarative profile matching | | isGroup(groups) | (AxisProfileGroupID | AxisProfileGroupID[]) => boolean | Group membership check | | onlyGroup(groups,fn) | (AxisProfileGroupID | AxisProfileGroupID[], fn:()=>void)=>void | Execute for matching groups | | unlessGroup(groups,fn) | (AxisProfileGroupID | AxisProfileGroupID[], fn:()=>void)=>void | Execute for non-matching groups | | matchGroup(cases,fallback) | (Array<[AxisProfileGroupID|AxisProfileGroupID[],T]>,T)=>T | Declarative group matching | | isTierRaw(tiers) | (AxisTier | AxisTier[]) => boolean | Raw tier check | | isTier(tiers) | (AxisTier | AxisTier[]) => boolean | Normalized tier check | | applyGlobalScaleVar(name?) | (cssVarName?:string)=>void | Writes scale factor to a CSS variable |


AxisCaps

| Field | Type | Meaning | |---|---|---| | touch | boolean | Touch-capable input detected | | hover | boolean | Hover-capable pointer detected | | anyPointer | "none" | "coarse" | "fine" | Highest fidelity pointer | | anyHover | "none" | "hover" | Hover support | | maxTouchPoints | number | Touch contact capacity | | isTouchLaptop | boolean | Touch + hover + fine pointer | | isHybrid | boolean | Hybrid input classification |


AxisAspect

| Field | Type | Description | |---|---|---| | ratio | number | Width / height | | shape | AxisShape | Shape classification | | isPortraitLike | boolean | Portrait-biased ratio | | isLandscapeLike | boolean | Landscape-biased ratio | | isSquareish | boolean | Near-square ratio | | isTall | boolean | Tall layout | | isWide | boolean | Wide layout | | isUltraWide | boolean | Ultra-wide layout | | pick(map) | (map) => number | Shape-based selector |


AxisProfile

| Field | Type | Description | |---|---|---| | id | AxisProfileID | Canonical profile string | | input | AxisInput | Input classification | | tier | AxisTier | Layout tier | | shape | AxisShape | Aspect shape | | orientation | AxisOrientation | Orientation | | isTouch | boolean | Touch input | | isPointer | boolean | Pointer input | | isHybrid | boolean | Hybrid input | | isCompact | boolean | Compact tier | | isComfortable | boolean | Comfortable tier | | isExpanded | boolean | Expanded tier | | isCinema | boolean | Cinema tier | | isTall | boolean | Tall shape | | isStandard | boolean | Standard shape | | isWide | boolean | Wide shape | | isUltraWide | boolean | Ultra-wide shape | | isSquareish | boolean | Squareish shape | | isPortrait | boolean | Portrait orientation | | isLandscape | boolean | Landscape orientation |


🧭 Profile Matrix (Canonical Identities)

Profile IDs exist for identity and grouping—not conditional logic by default.

Profiles follow:

<Input><Tier><Shape>

Total combinations: 60

Normalization rule: pointer + compact short-side windows normalize to comfortable.

| Input | Tier | Shape | Profile ID | |---|---|---|---| | Touch | Compact | Tall | TouchCompactTall | | Touch | Compact | Standard | TouchCompactStandard | | Touch | Compact | Wide | TouchCompactWide | | Touch | Compact | UltraWide | TouchCompactUltraWide | | Touch | Compact | Squareish | TouchCompactSquareish | | Touch | Comfortable | Tall | TouchComfortableTall | | Touch | Comfortable | Standard | TouchComfortableStandard | | Touch | Comfortable | Wide | TouchComfortableWide | | Touch | Comfortable | UltraWide | TouchComfortableUltraWide | | Touch | Comfortable | Squareish | TouchComfortableSquareish | | Touch | Expanded | Tall | TouchExpandedTall | | Touch | Expanded | Standard | TouchExpandedStandard | | Touch | Expanded | Wide | TouchExpandedWide | | Touch | Expanded | UltraWide | TouchExpandedUltraWide | | Touch | Expanded | Squareish | TouchExpandedSquareish | | Touch | Cinema | Tall | TouchCinemaTall | | Touch | Cinema | Standard | TouchCinemaStandard | | Touch | Cinema | Wide | TouchCinemaWide | | Touch | Cinema | UltraWide | TouchCinemaUltraWide | | Touch | Cinema | Squareish | TouchCinemaSquareish | | Pointer | Comfortable | Tall | PointerComfortableTall | | Pointer | Comfortable | Standard | PointerComfortableStandard | | Pointer | Comfortable | Wide | PointerComfortableWide | | Pointer | Comfortable | UltraWide | PointerComfortableUltraWide | | Pointer | Comfortable | Squareish | PointerComfortableSquareish | | Pointer | Expanded | Tall | PointerExpandedTall | | Pointer | Expanded | Standard | PointerExpandedStandard | | Pointer | Expanded | Wide | PointerExpandedWide | | Pointer | Expanded | UltraWide | PointerExpandedUltraWide | | Pointer | Expanded | Squareish | PointerExpandedSquareish | | Pointer | Cinema | Tall | PointerCinemaTall | | Pointer | Cinema | Standard | PointerCinemaStandard | | Pointer | Cinema | Wide | PointerCinemaWide | | Pointer | Cinema | UltraWide | PointerCinemaUltraWide | | Pointer | Cinema | Squareish | PointerCinemaSquareish | | Hybrid | Compact | Tall | HybridCompactTall | | Hybrid | Compact | Standard | HybridCompactStandard | | Hybrid | Compact | Wide | HybridCompactWide | | Hybrid | Compact | UltraWide | HybridCompactUltraWide | | Hybrid | Compact | Squareish | HybridCompactSquareish | | Hybrid | Comfortable | Tall | HybridComfortableTall | | Hybrid | Comfortable | Standard | HybridComfortableStandard | | Hybrid | Comfortable | Wide | HybridComfortableWide | | Hybrid | Comfortable | UltraWide | HybridComfortableUltraWide | | Hybrid | Comfortable | Squareish | HybridComfortableSquareish | | Hybrid | Expanded | Tall | HybridExpandedTall | | Hybrid | Expanded | Standard | HybridExpandedStandard | | Hybrid | Expanded | Wide | HybridExpandedWide | | Hybrid | Expanded | UltraWide | HybridExpandedUltraWide | | Hybrid | Expanded | Squareish | HybridExpandedSquareish | | Hybrid | Cinema | Tall | HybridCinemaTall | | Hybrid | Cinema | Standard | HybridCinemaStandard | | Hybrid | Cinema | Wide | HybridCinemaWide | | Hybrid | Cinema | UltraWide | HybridCinemaUltraWide | | Hybrid | Cinema | Squareish | HybridCinemaSquareish |


📏 AxisScale Reference

Axis exposes scaled numeric values and CSS-ready strings only.
No additional unit systems exist.
uiRem and uiPx act as semantic aliases.
Unit conversion stays explicit by design to prevent silent layout drift.

| Helper | Params | Returns | Purpose | |---|---:|---:|---| | factor | — | number | Global scale multiplier | | rem | (n:number) | string | Scaled rem | | px | (n:number) | number | Scaled pixels | | remRaw | (n:number) | number | Scaled rem (raw number) | | pxRaw | (n:number) | number | Scaled px (raw number) | | pxToRemRaw | (px:number) | number | Convert px to rem (raw) | | pxToRem | (px:number) | string | Convert px to rem | | remToPxRaw | (rem:number) | number | Convert rem to px (raw) | | remToPx | (rem:number) | number | Convert rem to px | | uiRem | (n:number) | string | Alias of rem | | uiPx | (n:number) | number | Alias of px | | clampRem | (min,ideal,max) | string | CSS clamp | | space | (step:number) | string | Spacing rhythm | | size | (step:number) | string | General sizing | | inset | (t,r,b,l) | string | Padding shorthand | | insetXY | (x,y) | string | Axis padding | | radius | (key) | string | Border radius | | border | (key) | string | Border width | | stroke | (key) | number | Stroke width | | shadow | (key) | string | Elevation | | blur | (key) | string | Blur radius | | font | (key) | string | Font size | | line | (key) | string | Line height | | tracking | (key) | string | Letter spacing | | weight | (key) | number | Font weight | | icon | (key) | number | Icon size | | controlHeight | (key) | number | Control height | | width | (key) | string | Width presets |


🧩 Essentials Reference

Essentials extend the snapshot surface area without changing core behavior. Axis core stays identical; Essentials add derived convenience fields.

Included fields:

  • viewport
  • safeArea
  • insets
  • controls
  • density
  • container
  • grid
  • type

🔌 Axis UI Runtime Reference

Axis UI derives from the snapshot and exposes on axis.ui.

AxisUI

| Member | Type | Description | |---|---|---| | mode | "auto" | "compact" | "comfortable" | "large" | Active UI mode | | factor | number | UI scale factor | | setMode(mode) | (mode:AxisUIMode | null) => void | Overrides UI mode | | getMode() | () => AxisUIMode | Reads active UI mode | | rem(value) | (value:number)=>string | UI rem helper | | px(value) | (value:number)=>number | UI px helper | | font(key) | (key:string)=>string | UI font helper | | space(step) | (step:number)=>string | UI spacing helper | | radius(key) | (key:string)=>string | UI radius helper | | hit() | ()=>number | UI minimum hit target | | hitRem() | ()=>string | UI minimum hit target (rem) |


🔌 Resolver Reference

Axis supports an optional resolver source installed on the engine.

Install a resolver

const engine = createAxisEngine();

engine.setResolver((axis, key) => {
     if (key === "theme.primary")
     {
          return "#1A73E8";
     }

     return null;
});

Resolve values

axis.resolve("theme.primary"); // string | number | boolean | null | object | array
axis.resolveString("theme.primary", "#000000"); // string

🧭 Profile Groups Reference

Axis exposes ergonomic profile groups for higher-level matching.

Group checks

axis.isGroup("mobileLike");
axis.isGroup(["touch", "compact"]);
axis.isGroup("keyboardOpen");

Group execution

axis.onlyGroup("mobileLike", () => {
     // touch-first stacked UI
});

axis.unlessGroup("keyboardOpen", () => {
     // normal bottom bar behavior
});

Group matching

const columns = axis.matchGroup([
     [["mobileLike"], 4],
     [["desktopLike"], 12]
], 8);

Built-in group IDs

touch, pointer, hybrid

compact, comfortable, expanded, cinema

tall, standard, wide, ultraWide, squareish

portrait, landscape

compactTouch, comfortableTouch, expandedTouch, cinemaTouch

compactPointer, comfortablePointer, expandedPointer, cinemaPointer

compactHybrid, comfortableHybrid, expandedHybrid, cinemaHybrid

mobileLike

desktopLike

keyboardOpen


🔀 Comparisons & Branching

Axis replaces conditional chaos with explicit, snapshot-driven branching.

Instead of guessing screen types or stacking breakpoints, logic reads directly from measured reality.

Snapshot Branching Reference

| Intent | Axis Signal | Use Instead of | |---|---|---| | Input class | axis.profile.isTouch | if (isMobile) | | Pointer precision | axis.caps.anyPointer | hover media queries | | Hybrid devices | axis.profile.isHybrid | device detection | | Density tier | axis.profile.isCompact | width breakpoints | | Layout structure | axis.layoutMode | grid breakpoint forks | | Aspect shape | axis.profile.isUltraWide | ratio math | | Orientation | axis.profile.isPortrait | orientation media queries | | Exact identity | axis.isProfile(...) | hard-coded layouts | | Keyboard open | axis.isGroup("keyboardOpen") | brittle keyboard listeners |


Profile Identity Comparison

Test one or more canonical profile IDs.

axis.isProfile("TouchCompactTall");

Multiple profiles:

axis.isProfile([
     "TouchCompactTall",
     "HybridComfortableWide"
]);

Input Class Checks (Touch / Pointer / Hybrid)

Axis exposes class-level booleans directly on the profile.

axis.profile.isTouch;
axis.profile.isPointer;
axis.profile.isHybrid;

Typical usage:

if (axis.profile.isTouch)
{
     // touch-first behavior
}

Tier-Based Branching

Tier flags replace breakpoint math entirely.

axis.profile.isCompact;
axis.profile.isComfortable;
axis.profile.isExpanded;
axis.profile.isCinema;

Example:

if (axis.profile.isCompact)
{
     // dense, stacked UI
}

Shape-Based Branching

Shape flags replace aspect-ratio thresholds.

axis.profile.isTall;
axis.profile.isStandard;
axis.profile.isWide;
axis.profile.isUltraWide;
axis.profile.isSquareish;

Example:

if (axis.profile.isUltraWide)
{
     // multi-column canvas layout
}

Orientation Checks

axis.profile.isPortrait;
axis.profile.isLandscape;

only — Execute for Matching Profiles

axis.only("TouchCompactTall", () => {
     // executes only for this profile
});

Multiple profiles:

axis.only([
     "TouchCompactTall",
     "HybridComfortableStandard"
], () => {
     // executes for any matching profile
});

unless — Execute for Non-Matching Profiles

axis.unless("PointerExpandedWide", () => {
     // executes for all except this profile
});

Predicate-Based Branching (onlyIf)

Use when logic depends on capability, not identity.

axis.onlyIf(a => a.caps.isTouchLaptop, () => {
     // hybrid laptop ergonomics
});

Declarative Matching (match)

Select values without branching trees.

const columns = axis.match([
     [["TouchCompactTall"], 4],
     [["TouchComfortableStandard"], 6],
     [["PointerExpandedWide"], 12]
], 8);
  • first match wins
  • fallback always required
  • no cascading if logic

Capability-Based Branching

if (axis.caps.hover && axis.caps.anyPointer === "fine")
{
     // hover-precise interactions
}
if (axis.caps.isHybrid)
{
     // dual-input ergonomics
}

⚙️ Snapshot Helper Methods

| Helper | Purpose | |---|---| | isProfile | Profile identity check | | only | Execute for matching profiles | | unless | Execute for non-matching profiles | | onlyIf | Predicate-based execution | | match | Declarative value selection | | isGroup | Group membership check | | onlyGroup | Execute for matching groups | | unlessGroup | Execute for non-matching groups | | matchGroup | Declarative group selection | | isTierRaw | Raw tier comparison | | isTier | Normalized tier comparison | | applyGlobalScaleVar | Write scale factor to CSS variable |


🧾 Usage Patterns

One-Off Snapshot

const axis = createAxisEngine().snapshot();

Reactive Subscription

const engine = createAxisEngine();

const unsubscribe = engine.subscribe(axis => {
     document.body.dataset.profile = axis.profile.id;
});

Conditional Logic

axis.only(["TouchCompactTall"], () => {});
axis.unless("PointerExpandedWide", () => {});

Predicate Logic

axis.onlyIf(a => a.caps.isTouchLaptop, () => {});

Declarative Matching

const density = axis.match([
     [["TouchCompactTall"], 1.1],
     [["PointerExpandedWide"], 0.95]
], 1.0);

Group Matching

const layout = axis.matchGroup([
     [["mobileLike"], "stack"],
     [["desktopLike"], "split"]
], "split");

Global Scale Variable

axis.applyGlobalScaleVar("--axis-scale");

Profile Override

engine.setProfileOverride("TouchCompactTall");
engine.setProfileOverride(null);

Resolver Usage

engine.setResolver((axis, key) => {
     (void axis);

     if (key === "layout.headerHeight")
     {
          return "64px";
     }

     return null;
});

const h = axis.resolveString("layout.headerHeight", "56px");

SSR / Node

const axis = createAxisEngine().snapshot();

Performance Notes

  • RAF-coalesced updates
  • minimal recomputation
  • deterministic snapshot rebuild
  • zero polling
  • safe-area measurer reused

📦 Installation

npm install @trap_stevo/axis

# or

yarn add @trap_stevo/axis
  • ESM
  • TypeScript included
  • framework-agnostic
  • no config
  • no flags
  • no CLI

⚡ Quick Start

Each Quick Start below renders the same UI, uses the same Axis signals, and makes the same layout decisions.

  • Shared intent across all examples
  • one Axis engine
  • one subscription
  • snapshot-driven layout
  • no breakpoints
  • no device detection
  • Axis → decision → CSS

Quick Start — Teaser

import createAxisEngine from "@trap_stevo/axis";

const engine = createAxisEngine();
const axis = engine.snapshot();

console.log(axis.input);
console.log(axis.tier);
console.log(axis.aspect.shape);

Shared Layout Decisions (All Examples)

Every Quick Start below uses these exact rules:

| Decision | Axis Signal | |---|---| | Layout structure | axis.layoutMode | | Grid columns | axis.grid.pickColumns(...) | | Page padding | axis.insets.contentPadding | | Typography | axis.type.size, axis.type.leading | | Controls | axis.controls.minHit() | | Identity | axis.profile.id | | UI sizing | axis.ui.space, axis.ui.hit |


Quick Start — React

import React, { useEffect, useMemo, useState } from "react";
import createAxisEngine from "@trap_stevo/axis";

export default function App()
{
     const engine = useMemo(() => createAxisEngine(), []);
     const [axis, setAxis] = useState(() => engine.snapshot());

     useEffect(() => {
          const unsubscribe = engine.subscribe(next => setAxis(next));

          return () => {
               unsubscribe();
               engine.destroy();
          };
     }, [engine]);

     const columns = axis.grid.pickColumns({
          compact : 4,
          comfortable : 8,
          expanded : 12,
          cinema : 12,
          default : 8
     });

     return (
          <div
               style={{
                    minHeight : axis.viewport.h(100, "dynamic"),
                    background : axis.caps.touch ? "rgb(10,20,40)" : "rgb(12,12,14)",
                    color : "white",
                    display : "flex",
                    justifyContent : "center"
               }}
          >
               <div
                    style={{
                         width : "100%",
                         maxWidth : axis.container.maxWidth,
                         padding : axis.insets.contentPadding,
                         boxSizing : "border-box"
                    }}
               >
                    <header
                         style={{
                              display : "flex",
                              justifyContent : "space-between",
                              alignItems : "center",
                              marginBottom : axis.ui.space(8)
                         }}
                    >
                         <div>
                              <div
                                   style={{
                                        fontSize : axis.type.size("2xl"),
                                        fontWeight : 700,
                                        lineHeight : axis.type.leading("tight")
                                   }}
                              >
                                   Axis Quick Start
                              </div>

                              <div style={{ opacity : 0.75 }}>
                                   profile : {axis.profile.id}
                              </div>
                         </div>

                         <button
                              style={{
                                   minHeight : axis.ui.hit() + "px",
                                   padding : axis.scale.insetXY(1.0, 0.75),
                                   borderRadius : axis.ui.radius("xl"),
                                   border : "none",
                                   background : "#1A73E8",
                                   color : "white",
                                   cursor : "pointer"
                              }}
                         >
                              Action
                         </button>
                    </header>

                    <main
                         style={{
                              display : "grid",
                              gridTemplateColumns :
                                   axis.layoutMode === "stack"
                                        ? "1fr"
                                        : `repeat(${columns}, 1fr)`,
                              gap : axis.grid.gutter
                         }}
                    >
                         {Array.from({ length : columns }).map((_, i) => (
                              <div
                                   key={String(i)}
                                   style={{
                                        padding : axis.container.cardPadding,
                                        borderRadius : axis.scale.radius("2xl"),
                                        background : "rgba(255,255,255,0.06)"
                                   }}
                              >
                                   Card {i + 1}
                              </div>
                         ))}
                    </main>
               </div>
          </div>
     );
}

Quick Start — HTML / Vanilla DOM (Canonical Reference)

<!DOCTYPE html>
<html lang="en">
<head>
     <meta charset="UTF-8" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
     <title>Axis Quick Start</title>
</head>
<body>
     <div id="root"></div>

     <script type="module">
          import createAxisEngine from "@trap_stevo/axis";

          const engine = createAxisEngine();
          const root = document.getElementById("root");

          function render(axis)
          {
               const columns = axis.grid.pickColumns({
                    compact : 4,
                    comfortable : 8,
                    expanded : 12,
                    cinema : 12,
                    default : 8
               });

               root.innerHTML = `
                    <div style="
                         min-height:${axis.viewport.h(100, "dynamic")};
                         background:${axis.caps.touch ? "rgb(10,20,40)" : "rgb(12,12,14)"};
                         color:white;
                         display:flex;
                         justify-content:center;
                    ">
                         <div style="
                              width:100%;
                              max-width:${axis.container.maxWidth};
                              padding:${axis.insets.contentPadding};
                              box-sizing:border-box;
                         ">
                              <header style="
                                   display:flex;
                                   justify-content:space-between;
                                   align-items:center;
                                   margin-bottom:${axis.ui.space(8)};
                              ">
                                   <div>
                                        <div style="
                                             font-size:${axis.type.size("2xl")};
                                             font-weight:700;
                                        ">
                                             Axis Quick Start
                                        </div>
                                        <div style="opacity:0.75">
                                             profile : ${axis.profile.id}
                                        </div>
                                   </div>

                                   <button style="
                                        min-height:${axis.ui.hit()}px;
                                        padding:${axis.scale.insetXY(1.0,0.75)};
                                        border-radius:${axis.ui.radius("xl")};
                                        background:#1A73E8;
                                        color:white;
                                        border:none;
                                        cursor:pointer;
                                   ">
                                        Action
                                   </button>
                              </header>

                              <main style="
                                   display:grid;
                                   grid-template-columns:${axis.layoutMode === "stack"
                                        ? "1fr"
                                        : `repeat(${columns},1fr)`};
                                   gap:${axis.grid.gutter};
                              ">
                                   ${Array.from({ length : columns }).map((_, i) => `
                                        <div style="
                                             padding:${axis.container.cardPadding};
                                             border-radius:${axis.scale.radius("2xl")};
                                             background:rgba(255,255,255,0.06);
                                        ">
                                             Card ${i + 1}
                                        </div>
                                   `).join("")}
                              </main>
                         </div>
                    </div>
               `;
          }

          const unsubscribe = engine.subscribe(render);

          window.addEventListener("beforeunload", () => {
               unsubscribe();
               engine.destroy();
          });
     </script>
</body>
</html>

Quick Start — HTML / Vanilla DOM (Axis UI Reference)

<!DOCTYPE html>
<html lang="en">
<head>
     <meta charset="UTF-8" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
     <title>Axis Demo</title>
     <style>
          :root
          {
               font-family : system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
          }

          body
          {
               margin : 0;
          }

          .app
          {
               min-height : 100vh;
               display : flex;
               justify-content : center;
               align-items : stretch;
          }

          .shell
          {
               width : 100%;
               display : flex;
               flex-direction : column;
               box-sizing : border-box;
          }

          .row
          {
               display : flex;
               align-items : center;
               justify-content : space-between;
               gap : 12px;
          }

          .card
          {
               box-sizing : border-box;
               width : 100%;
          }

          .pill
          {
               display : inline-flex;
               align-items : center;
               gap : 10px;
               padding : 8px 12px;
               border-radius : 999px;
               white-space : nowrap;
          }

          .grid
          {
               display : grid;
               width : 100%;
               box-sizing : border-box;
          }

          .btn
          {
               border : none;
               cursor : pointer;
               user-select : none;
          }

          .muted
          {
               opacity : 0.78;
          }

          .mono
          {
               font-family : ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
          }
     </style>
</head>
<body>
     <div id="root"></div>

     <script type="module">
          import createAxisEngine from "../../dist/index.js";

          const axis = createAxisEngine();

          const root = document.getElementById("root");

          axis.setResolver(function(axisSnapshot, key)
          {
               if (key === "bg")
               {
                    return axisSnapshot.caps.touch
                         ? "rgba(10, 20, 40, 1)"
                         : "rgba(12, 12, 14, 1)";
               }

               if (key === "card")
               {
                    return "rgba(255, 255, 255, 0.06)";
               }

               if (key === "border")
               {
                    return "rgba(255, 255, 255, 0.10)";
               }

               if (key === "text")
               {
                    return "white";
               }

               if (key === "muted")
               {
                    return "rgba(255, 255, 255, 0.78)";
               }

               if (key === "accent")
               {
                    return "#1A73E8";
               }

               return null;
          });

          function el(tag, props, children)
          {
               const node = document.createElement(tag);

               if (props)
               {
                    for (const key of Object.keys(props))
                    {
                         if (key === "style")
                         {
                              Object.assign(node.style, props.style);
                              continue;
                         }

                         if (key.startsWith("on") && typeof props[key] === "function")
                         {
                              node.addEventListener(key.slice(2).toLowerCase(), props[key]);
                              continue;
                         }

                         node.setAttribute(key, String(props[key]));
                    }
               }

               if (Array.isArray(children))
               {
                    for (const child of children)
                    {
                         if (typeof child === "string")
                         {
                              node.appendChild(document.createTextNode(child));
                         }
                         else if (child)
                         {
                              node.appendChild(child);
                         }
                    }
               }

               return node;
          }
          
          function render(snapshot)
          {
               const ui = snapshot.ui;
               
               const bg = snapshot.resolveString("bg", "rgba(12, 12, 14, 1)");
               const cardBg = snapshot.resolveString("card", "rgba(255, 255, 255, 0.06)");
               const border = snapshot.resolveString("border", "rgba(255, 255, 255, 0.10)");
               const text = snapshot.resolveString("text", "white");
               const muted = snapshot.resolveString("muted", "rgba(255, 255, 255, 0.78)");
               const primary = snapshot.resolveString("accent", "#1A73E8");

               const padding = snapshot.insets
                    ? snapshot.insets.contentPadding
                    : snapshot.scale.insetXY(1.25, 1.25);

               const shell = el("div", {
                    class : "app",
                    style : {
                         background : bg,
                         color : text
                    }
               }, [
                    el("div", {
                         class : "shell",
                         style : {
                              maxWidth : snapshot.container.maxWidth,
                              padding : padding
                         }
                    }, [
                         el("div", {
                              class : "row",
                              style : { marginBottom : snapshot.scale.space(8) }
                         }, [
                              el("div", null, [
                                   el("div", {
                                        style : {
                                             fontSize : ui.font("2xl"),
                                             fontWeight : "700",
                                             letterSpacing : snapshot.scale.tracking("tight"),
                                             lineHeight : snapshot.scale.line("tight")
                                        }
                                   }, ["Axis Demo"]),
                                   el("div", {
                                        style : {
                                             marginTop : snapshot.scale.space(2),
                                             fontSize : ui.font("md"),
                                             lineHeight : snapshot.scale.line("normal"),
                                             color : muted
                                        }
                                   }, ["Universal interface intelligence without breakpoint soup."])
                              ]),
                              el("div", null, [
                                   el("span", {
                                        class : "pill mono",
                                        style : {
                                             border : "1px solid " + border,
                                             background : "rgba(0, 0, 0, 0.25)",
                                             padding : snapshot.scale.insetXY(0.75, 0.50),
                                             fontSize : ui.font("sm")
                                        }
                                   }, [
                                        "profile : " + snapshot.profile.id
                                   ])
                              ])
                         ]),

                         el("div", {
                              class : "grid",
                              style : {
                                   gridTemplateColumns : snapshot.layoutMode === "stack" ? "1fr" : "1fr 1fr",
                                   gap : snapshot.scale.space(8)
                              }
                         }, [
                              el("div", {
                                   class : "card",
                                   style : {
                                        border : "1px solid " + border,
                                        background : cardBg,
                                        borderRadius : snapshot.scale.radius("2xl"),
                                        padding : snapshot.container.cardPadding,
                                        boxShadow : snapshot.scale.shadow("lg")
                                   }
                              }, [
                                   el("div", {
                                        style : {
                                             fontSize : ui.font("xl"),
                                             fontWeight : "700",
                                             lineHeight : snapshot.scale.line("snug")
                                        }
                                   }, ["Signals"]),
                                   el("div", {
                                        class : "mono",
                                        style : {
                                             marginTop : snapshot.scale.space(4),
                                             fontSize : ui.font("sm"),
                                             lineHeight : snapshot.scale.line("relaxed"),
                                             color : muted
                                        }
                                   }, [
                                        "caps.touch : " + String(snapshot.caps.touch) + "\n" +
                                        "caps.hover : " + String(snapshot.caps.hover) + "\n" +
                                        "orientation : " + snapshot.orientation + "\n" +
                                        "input : " + snapshot.input + "\n" +
                                        "tier : " + snapshot.tier + "\n" +
                                        "shape : " + snapshot.aspect.shape + "\n" +
                                        "aspect : " + snapshot.aspect.ratio.toFixed(3) + "\n" +
                                        "scale.factor : " + snapshot.scale.factor.toFixed(3) + "\n" +
                                        "ui.mode : " + ui.mode + "\n" +
                                        "ui.factor : " + ui.factor.toFixed(3)
                                   ])
                              ]),

                              el("div", {
                                   class : "card",
                                   style : {
                                        border : "1px solid " + border,
                                        background : cardBg,
                                        borderRadius : snapshot.scale.radius("2xl"),
                                        padding : snapshot.container.cardPadding,
                                        boxShadow : snapshot.scale.shadow("lg")
                                   }
                              }, [
                                   el("div", {
                                        style : {
                                             fontSize : ui.font("xl"),
                                             fontWeight : "700",
                                             lineHeight : snapshot.scale.line("snug")
                                        }
                                   }, ["One UI, Many Screens"]),
                                   el("div", {
                                        style : {
                                             marginTop : snapshot.scale.space(4),
                                             fontSize : ui.font("md"),
                                             lineHeight : snapshot.scale.line("relaxed"),
                                             maxWidth : snapshot.type.measure.maxLineWidth,
                                             color : muted
                                        }
                                   }, [
                                        "AxisUI determines spacing, typography, and control sizing continuously — without breakpoints or platform branches."
                                   ]),

                                   el("div", {
                                        style : {
                                             display : "flex",
                                             gap : snapshot.scale.space(4),
                                             marginTop : snapshot.scale.space(7),
                                             flexWrap : "wrap"
                                        }
                                   }, [
                                        el("button", {
                                             class : "btn",
                                             onClick : function()
                                             {
                                                  alert(
                                                       "AxisUI hit target : " +
                                                       ui.hit() + "px (" + ui.hitRem() + ")"
                                                  );
                                             },
                                             style : {
                                                  background : primary,
                                                  color : "white",
                                                  borderRadius : snapshot.scale.radius("xl"),
                                                  padding : snapshot.scale.insetXY(1.00, 0.75),
                                                  fontSize : ui.font("md"),
                                                  minHeight : ui.hit() + "px",
                                                  boxShadow : snapshot.scale.shadow("md")
                                             }
                                        }, ["Action"]),
                                        el("div", {
                                             class : "pill",
                                             style : {
                                                  border : "1px solid " + border,
                                                  background : "rgba(0, 0, 0, 0.20)",
                                                  borderRadius : snapshot.scale.radius("pill"),
                                                  padding : snapshot.scale.insetXY(0.75, 0.55),
                                                  fontSize : ui.font("sm"),
                                                  color : muted
                                             }
                                        }, [
                                             "ui.mode : " + ui.mode
                                        ])
                                   ])
                              ])
                         ])
                    ])
               ]);

               root.innerHTML = "";
               root.appendChild(shell);
          }

          axis.subscribe(render);
     </script>
</body>
</html>

Quick Start — Vue (Vue 3, Composition API)

<template>
     <div
          :style="{
               minHeight : axis.viewport.h(100, 'dynamic'),
               background : axis.caps.touch ? 'rgb(10,20,40)' : 'rgb(12,12,14)',
               color : 'white',
               display : 'flex',
               justifyContent : 'center'
          }"
     >
          <div
               :style="{
                    width : '100%',
                    maxWidth : axis.container.maxWidth,
                    padding : axis.insets.contentPadding,
                    boxSizing : 'border-box'
               }"
          >
               <header
                    :style="{
                         display : 'flex',
                         justifyContent : 'space-between',
                         alignItems : 'center',
                         marginBottom : axis.ui.space(8)
                    }"
               >
                    <div>
                         <div
                              :style="{
                                   fontSize : axis.type.size('2xl'),
                                   fontWeight : 700
                              }"
                         >
                              Axis Quick Start
                         </div>

                         <div style="opacity:0.75">
                              profile : {{ axis.profile.id }}
                         </div>
                    </div>

                    <button
                         :style="{
                              minHeight : axis.ui.hit() + 'px',
                              padding : axis.scale.insetXY(1.0,0.75),
                              borderRadius : axis.ui.radius('xl'),
                              background : '#1A73E8',
                              color : 'white',
                              border : 'none',
                              cursor : 'pointer'
                         }"
                    >
                         Action
                    </button>
               </header>

               <main
                    :style="{
                         display : 'grid',
                         gridTemplateColumns :
                              axis.layoutMode === 'stack'
                                   ? '1fr'
                                   : `repeat(${columns},1fr)`,
                         gap : axis.grid.gutter
                    }"
               >
                    <div
                         v-for="n in columns"
                         :key="n"
                         :style="{
                              padding : axis.container.cardPadding,
                              borderRadius : axis.scale.radius('2xl'),
                              background : 'rgba(255,255,255,0.06)'
                         }"
                    >
                         Card {{ n }}
                    </div>
               </main>
          </div>
     </div>
</template>

<script setup>
import { computed, onBeforeUnmount, ref } from "vue";
import createAxisEngine from "@trap_stevo/axis";

const engine = createAxisEngine();
const axis = ref(engine.snapshot());

const unsubscribe = engine.subscribe(next => {
     axis.value = next;
});

onBeforeUnmount(() => {
     unsubscribe();
     engine.destroy();
});

const columns = computed(() => {
     return axis.value.grid.pickColumns({
          compact : 4,
          comfortable : 8,
          expanded : 12,
          cinema : 12,
          default : 8
     });
});
</script>

📜 License

SCL-1.0-Universal


🧭 One Engine. Every Interface.

Axis provides a single source of truth for interface reality.
From phones to desktops to ultra-wide canvases, layout decisions remain explicit, deterministic, and reusable.

Every device. Every shape.
One layout mind.