# jstyle

> Programmatic CSS builder

Latest version **0.4.0** (published 2026-06-04) · (MIT OR Apache-2.0) license · 0 weekly downloads

## Install

```sh
npm install jstyle
pnpm add jstyle
yarn add jstyle
bun add jstyle
```

## Health

**Score 60/100 (C)** — status: active.

Positive: esm support; no vulnerabilities; has provenance; high maintenance score.

Warnings: low downloads; no types; pre 1.0.

## Facts

| | |
|---|---|
| Version | 0.4.0 |
| Published | 2026-06-04 |
| First published | 2015-08-17 |
| Weekly downloads | 0 |
| License | (MIT OR Apache-2.0) |
| TypeScript types | none |
| Module format | ESM |
| Dependencies | 2 |
| Unpacked size | 588.9 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Provenance | attested (GitHub Actions) |
| GitHub stars | 0 |
| Author | Boris Kaul |
| Maintainers | localvoid |
| Keywords | css |

## Links

- npm: https://www.npmjs.com/package/jstyle
- Repository: https://github.com/localvoid/jstyle
- Issues: https://github.com/localvoid/jstyle/issues
- npm.io page: https://npm.io/package/jstyle

## Dependencies (2)

- [assetcraft](https://npm.io/package/assetcraft.md) ^0.1.2
- [lightningcss](https://npm.io/package/lightningcss.md) ^1.32.0

## Alternatives

- [style-dictionary](https://npm.io/package/style-dictionary.md) — 2.0M weekly downloads
- [postcss-merge-idents](https://npm.io/package/postcss-merge-idents.md) — 1.7M weekly downloads
- [@fontsource/noto-sans](https://npm.io/package/@fontsource/noto-sans.md) — 93.0K weekly downloads
- [uglifycss](https://npm.io/package/uglifycss.md) — 71.6K weekly downloads
- [mat4-interpolate](https://npm.io/package/mat4-interpolate.md) — 23.3K weekly downloads

## Recent versions

- 0.4.0 (latest) — 2026-06-04
- 0.2.0 — 2016-05-25
- 0.1.1 — 2015-08-17
- 0.1.0 — 2015-08-17

## README

# jstyle

TypeScript-first CSS builder.

## Features

- **Scoped Identifiers** — Class names, CSS vars and animation names are isolated
- **Type-Safe Properties** — All CSS properties are typed
- **Minified Identifiers** — Generates short, unique identifiers (class names, vars, …)
- **Deterministic Builds** — Minified identifiers are stored in a map file
- **Multi-Language Support** — Outputs modules with constant identifiers for TypeScript and Rust
- **Class Maps** — Efficient (interned strings) runtime state to class name mapping for conditional styles
- **CSS Minification** — Built-in minification via [Lightning CSS](https://lightningcss.dev/)
- **Manifest File** — Supports [assetcraft](https://github.com/localvoid/assetcraft) manifest files (resolve urls and emit)

## Installation

```bash
npm install jstyle
# or
bun add jstyle
```

## Quick Start

```ts
import { ns, style, emit } from 'jstyle';
import * as p from 'jstyle/props';

const APP = ns('app');

const BUTTON = APP.class('button');

const rules = [
  style(BUTTON, [p.display('inline-flex'), p.padding('8px 16px'), p.backgroundColor('blue')]),
  style(BUTTON.hover, [p.backgroundColor('darkblue')]),
];

await emit({
  input: [{ name: 'app', build: async () => ({ css: rules }) }],
  outDir: './dist',
  renderURL: (name, hash) => `/assets/${name}.${hash}.css`,
});
```

## Imports

```ts
import { ns, style, media, $, env, important } from 'jstyle'; // Core types and factories
import * as p from 'jstyle/props'; // CSS property constructors
import { emit } from 'jstyle/emit'; // Emit orchestrator
import { JSEmitter } from 'jstyle/emit/js'; // JS emitter
import { RustEmitter } from 'jstyle/emit/rust'; // Rust emitter
```

## Core Concepts

### Namespaces

Each module gets its own namespace to avoid identifier collisions:

```ts
import { ns } from 'jstyle';

const SCAFFOLD = ns('app.scaffold');
const CONTAINER = SCAFFOLD.class('container');
```

### Properties

Typed constructors from `jstyle/props`:

```ts
import * as p from 'jstyle/props';

p.display('grid');
p.margin('0 auto');
p.color('#333');

// Predefined constants
p.FLEX; // display: flex
p.BORDER_BOX; // box-sizing: border-box
```

### Value Helpers

```ts
// CSS variables
const SPACING = NS.dashedIdent('spacing');
style('section', [p.padding($(SPACING))]); // padding: var(--spacing);

// Environment variables
style('input', [p.padding(env('safe-area-inset-top'))]);

// !important
style('modal', [important(p.zIndex('9999'))]); // z-index: 9999 !important;
```

### Selectors

Pseudo-classes and pseudo-elements via getter properties:

```ts
const btn = NS.class('button');
btn.hover; // :hover
btn.before; // ::before
btn.nthChild('2n+1'); // :nth-child(2n+1)
btn.has(p.color('red')); // :has(.red)
```

Combinators for complex selectors:

```ts
const CARD = NS.class('card');
const CARD_TITLE = CARD.join('title');

CARD.descendant(CARD_TITLE); // .card .card_title
CARD.child(CARD_TITLE); // .card > .card_title
```

### At-Rules

Factory functions for all CSS at-rules:

```ts
import { media, container, keyframes, layer, supports, fontFace } from 'jstyle';

media('(min-width: 768px)', [style(container, [p.margin('0')])]);
keyframes('fade', [pct(0, [p.opacity('0')]), pct(100, [p.opacity('1')])]);
layer('utilities', [style(highlight, [p.backgroundColor('yellow')])]);
fontFace([p.fontFamily('MyFont'), p.src('url(font.woff2)')]);
```

### Size and Color Utilities

```ts
import { Size, rgba } from 'jstyle';

Size.px(16); // 16px
Size.rem(2); // 2rem

rgba(255, 0, 0, 1); // rgba(255, 0, 0, 1)
```

### Class Maps

Efficient runtime state to class name mapping:

```ts
const buttonMap = NS.classMap({
  base: BUTTON,
  states: {
    hovered: BUTTON_HOVER,
    active: BUTTON_ACTIVE,
    size: [null, BUTTON_SM, BUTTON_MD, BUTTON_LG],
  },
  exclude: (state) => state.get('hovered') === true && state.get('active') === true,
});
```

## Emitting

```ts
import { emit } from 'jstyle/emit';
import { JSEmitter } from 'jstyle/emit/js';
import { RustEmitter } from 'jstyle/emit/rust';

await emit({
  input: [
    {
      name: 'app',
      build: async (ctx) => ({
        css: [
          /* your rules */
        ],
      }),
    },
  ],
  outDir: './dist',
  renderURL: (name, sha) => `/assets/${name}.${sha}.css`,
  emit: [
    new JSEmitter({ outDir: './packages/css/src', clean: true }), // optional: emit JS modules
    new RustEmitter({ outDir: './crates/css', clean: true }), // optional: emit Rust module
  ],
  minify: true, // optional: minify CSS
  map: './dist/.cssmap.json', // optional: persist ID mappings
});
```

## Output Formats

- **CSS** — Always emitted
- **JS** — Via `JSEmitter` from `jstyle/emit/js`: identifier bindings + TypeScript declarations
- **Rust** — Via `RustEmitter` from `jstyle/emit/rust`: identifier bindings
- **Custom** — Implement the `Emitter` interface from `jstyle/emit/emitter` for custom output formats

## License

MIT OR Apache-2.0

---
_Source: https://npm.io/package/jstyle · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
