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

solid-snap

v0.1.0

Published

SolidJS library for constrained, grid-based 2D layouts.

Readme

solid-snap

GitHub npm version

SolidJS library for creating constrained, grid-based 2D layouts.

Solid Snap gives rectangular items freedom within a logical grid. It handles snapping, blocking collisions, surface boundaries, resize constraints, keyboard movement, and deterministic JSON validation while leaving application state with the consumer.

Installation

npm install solid-snap solid-js

The package is written in TypeScript and publishes ES modules with generated declaration files. solid-js is a peer dependency; interactjs supplies pointer and touch interaction mechanics.

Controlled usage

Grid.layout is the canonical geometry state. GridItem children are keyed by ID and provide content plus interaction constraints.

import { createSignal } from "solid-js";
import { Grid, GridItem, type GridLayout } from "solid-snap";
import "solid-snap/style.css";

const initialLayout: GridLayout = {
  columns: 48,
  rows: 32,
  items: [
    { id: "headline", x: 0, y: 0, w: 16, h: 8 },
    { id: "hero", x: 24, y: 8, w: 16, h: 16 }
  ]
};

function Editor() {
  const [layout, setLayout] = createSignal(initialLayout);

  return (
    <Grid
      layout={layout()}
      snap={8}
      showGrid
      onChange={setLayout}
      onSelect={(id) => console.log("selected", id)}
    >
      <GridItem id="headline" minW={8} minH={4}>
        <h1>Headline</h1>
      </GridItem>
      <GridItem id="hero" minW={8} minH={8}>
        <div>Hero</div>
      </GridItem>
    </Grid>
  );
}

The parent surface needs a size. Its logical geometry scales to any CSS dimensions without changing the persisted layout:

.editor-surface {
  width: min(100%, 960px);
  aspect-ratio: 16 / 9;
}

Grid also accepts optional columns and rows overrides when a canonical layout is being displayed at a different effective density. Changing snap never rewrites existing item geometry.

Public layout API

interface GridRect {
  x: number;
  y: number;
  w: number;
  h: number;
}

interface GridItemData extends GridRect {
  id: string;
}

interface GridLayout {
  columns: number;
  rows: number;
  items: GridItemData[];
}

Core helpers are pure TypeScript and can run without a DOM:

import {
  canPlace,
  convertLayout,
  findCollisions,
  isInsideBounds,
  overlaps,
  validateLayout
} from "solid-snap";

For a server-side or command-line validator, the DOM-free entry point is also available explicitly:

import { validateLayout } from "solid-snap/core";

Adjacent rectangles are valid. Colliding items block the candidate and are never automatically moved or repacked.

Validation and JSON generation

const result = validateLayout(layout);

if (!result.valid) {
  for (const error of result.errors) {
    console.log(error.code, error.itemId);
  }
}

Validation errors are machine-readable: OUT_OF_BOUNDS, COLLISION, INVALID_SIZE, INVALID_COORDINATE, and DUPLICATE_ID. The exported gridLayoutSchema describes the integer-only JSON format for external or LLM-generated layouts.

Grid density conversion is lossless by default:

const canonical = convertLayout(layout, { columns: 48, rows: 32 });
const smaller = convertLayout(canonical, { columns: 6, rows: 4 });

Lossy conversion throws LayoutConversionError unless { allowRounding: true } is passed explicitly.

GridItem options

GridItem supports minW, minH, maxW, maxH, draggable, resizable, locked, disabled, selected, and ariaLabel. Resize handles are available on all four edges and four corners when resizing is enabled.

Keyboard arrows move the selected/focused item by one snap increment. Shift plus an arrow moves by four increments. Keyboard candidates use the same bounds and collision engine as pointer interactions.

Grid emits onDragStart, onDrag, onDragEnd, onResizeStart, onResize, and onResizeEnd events. Each event includes the item ID, current rectangle, previous rectangle, original rectangle, and resize direction when applicable.

Styling

The library ships sensible headless defaults in solid-snap/style.css. Customize these variables or override the documented class names:

.solid-snap {
  --solid-snap-line-color: rgb(14 165 233 / 0.25);
  --solid-snap-selection-color: #f97316;
  --solid-snap-handle-size: 10px;
}

Grid and GridItem also accept a style prop, so custom properties can travel with a specific surface or item without overriding its controlled geometry:

<Grid style={{ "--solid-snap-selection-color": "#fbbf24" }} /* … */>
  <GridItem id="hero" style={{ "--card-image": "url('/hero.jpg')" }}>
    <article class="card-with-image">Hero</article>
  </GridItem>
</Grid>

For state that belongs to an application item rather than its geometry, use GridItem.render. It receives the stable grid item ID, so it can look up and render arbitrary content—such as a table—without adding non-geometric data to GridLayout:

<Grid layout={layout()} onChange={setLayout}>
  <GridItem
    id="sales-table"
    render={(id) => <SalesTable data={itemData()[id]} />}
  />
</Grid>

render takes precedence over static children if both are provided.

Grid visibility is independent of snapping: showGrid={false} hides lines while snap continues to constrain interactions.

Examples and landing page

Run the interactive SolidJS demo:

npm run dev -- --config examples/basic/vite.config.ts

For a single development server with several focused playgrounds (basic layout, item constraints, and interaction events), run:

npm run dev:examples

Build the demo or the standalone landing page:

npm run build:demo
npm run build:examples
npm run build:landing

The landing page includes a live playground, reusable SVG logo, and links to the project source at github.com/only-cliches/solid-snap.

GitHub Pages

The marketing site deploys from main through the Deploy landing site to GitHub Pages workflow. Set Settings → Pages → Source to GitHub Actions after the repository is renamed; deployment targets:

https://only-cliches.github.io/solid-snap/

The workflow builds with the /solid-snap/ asset base required for a project Pages site. Local npm run dev:landing remains available at /.

Development

npm install
npm test
npm run typecheck
npm run build
npm run qa:e2e

qa:e2e starts the demo and uses the local agent-browser development dependency to verify the controls, JSON validation, keyboard movement, pointer drag, and pointer resize flows in headless Chromium. It requires Chrome or Chromium to be available on the host.

The library intentionally excludes layers, application-specific data, arbitrary pixel positioning, rotation, freeform overlap, automatic packing, multi-selection, grouping, undo/redo, animation, and AI calls.