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

@hasan894/htss

v1.0.1

Published

htss is an esoteric language like css that transpiles to html

Downloads

34

Readme

HTSS

HTSS (HTML Style Sheet) is a custom templating language that looks like CSS but compiles to HTML. Write your markup in a clean, expressive syntax inspired by CSS selectors — variables, conditionals, loops, components, and string interpolation — and get valid HTML out.

$name: "World";

div.hero {
  data: "Hello $name";

  @for $i from 1 to 3 {
    span { data: "Item $i"; }
  }
}

Compiles to:

<div class="hero">
  Hello World
  <span>Item 1</span>
  <span>Item 2</span>
  <span>Item 3</span>
</div>

Table of Contents


Installation

npm

npm install @hasan894/htss
import { HTSS } from "@hasan894/htss";

const html = HTSS.run(`h1 { data: "Hello"; }`);
console.log(html);

Browser (CDN)

No build step needed. Import directly from jsDelivr:

<script type="module">
  import { HTSS } from "https://cdn.jsdelivr.net/npm/@hasan894/[email protected]/dist/index.js";

  document.body.innerHTML = HTSS.run(`
    h1 { data: "Hello from HTSS!"; }
  `);
</script>

Quick Start

import { HTSS } from "@hasan894/htss";

const code = `
  $title: "My Page";

  body {
    h1 { data: $title; }
    p  { data: "Welcome to HTSS."; }
  }
`;

// Compile to a full HTML document (with <!DOCTYPE html> wrapper)
const html = HTSS.run(code, false);
console.log(html);

HTSS.run(code, debug=false)

Compiles HTSS source code all the way to an HTML string.

| Param | Type | Description | |---------|-----------|------------------------------------------------------------------------------| | code | string | The HTSS source code to compile. | | debug | boolean | When true, prints the token stream, AST, and transformed node tree to the console. |

Returns a string containing a complete HTML document.

HTSS.AST(code, debug)

Returns the raw Abstract Syntax Tree (a BlockNode) without generating HTML. Useful for tooling and analysis.


Language Reference

Selectors

Selectors mirror CSS syntax and define an HTML element. Every selector maps to one HTML tag.

div { }

Classes

Attach one or more classes with .:

div.card { }
div.card.featured { }

Renders as <div class="card featured">.

IDs

Attach an id with #:

section#main { }

Renders as <section id="main">.

Variable Classes and IDs

Classes and IDs can reference variables using $:

$theme: "dark";
$index: 1;

div.$theme#$index { }

Renders as <div class="dark" id="1">.

Nesting

Selectors nest directly inside other selectors to create parent–child relationships:

ul {
  li { data: "First"; }
  li { data: "Second"; }
}
<ul>
  <li>First</li>
  <li>Second</li>
</ul>

Nesting depth is unlimited.

Self-Closing Tags

The following tags are automatically rendered self-closing when they have no text or children: area, base, br, col, embed, hr, img, input, link, meta, source, track, wbr.

br { }
input { type: "text"; }
<br />
<input type="text" />

Properties & Attributes

Any key–value pair written inside a selector becomes an HTML attribute:

a {
  href: "https://example.com";
  target: "_blank";
}
<a href="https://example.com" target="_blank"></a>

The semicolon at the end of a property is optional but recommended.

Boolean Attributes

If a property value evaluates to true, the attribute is emitted as a bare presence attribute. If it evaluates to false, the attribute is omitted entirely.

input {
  type: "checkbox";
  checked: true;
  disabled: false;
}
<input type="checkbox" checked />

Text Content

The special property data sets the inner text of an element:

h1 { data: "Hello, World!"; }
p  { data: "A paragraph."; }
<h1>Hello, World!</h1>
<p>A paragraph.</p>

Text content is automatically HTML-escaped — characters like <, >, &, and " are safe to use in data values.

When an element has both a data value and child selectors, the text appears before the children.


Variables

Variables are declared with $ and assigned with :. They can hold strings, numbers, or booleans.

$count: 10;
$label: "Items";
$active: true;

Variables are scoped: a variable declared inside an element body is not accessible outside it, but inner scopes can read and mutate variables from outer scopes.

$x: 1;

div {
  $y: 2;            /* $y is local to this div */
  span { data: $x; } /* $x is accessible here */
  $x: $x + 1;        /* mutates the outer $x */
}

/* $x is now 2 here; $y is not accessible */
h1 { data: $x; }

Reassigning a variable uses the same syntax as declaring one:

$i: 0;
$i: $i + 1;  /* $i is now 1 */

String Interpolation

Embed variable values directly inside strings using $variableName:

$name: "Alice";
$age: 30;

p { data: "Hello, $name! You are $age years old."; }
<p>Hello, Alice! You are 30 years old.</p>

Multiple variables can appear in the same string:

$first: "John";
$last: "Doe";

h2 { data: "Welcome, $first $last."; }

Expressions & Operators

Expressions can appear as the value of any property or variable.

Arithmetic

| Operator | Operation | Example | Result | |----------|----------------|---------------|--------| | + | Addition / Concat | 2 + 3 | 5 | | - | Subtraction | 10 - 4 | 6 | | * | Multiplication | 3 * 4 | 12 | | / | Division | 10 / 2 | 5 | | % | Modulo | 7 % 3 | 1 |

+ also concatenates strings and numbers:

$i: 3;
div { style: "font-size: " + $i * 10 + "px"; }
/* → style="font-size: 30px" */

Standard operator precedence applies: *, /, % bind tighter than +, -. Use parentheses to override:

div { data: (2 + 3) * 4; }  /* → 20 */
div { data: 2 + 3 * 4;     }  /* → 14 */

Comparison

Used primarily in @if and @loop conditions:

| Operator | Meaning | |----------|-----------------------| | == | Equal | | != | Not equal | | < | Less than | | > | Greater than | | <= | Less than or equal | | >= | Greater than or equal |

Logical

| Operator | Meaning | |----------|---------| | && | AND | | \|\| | OR | | ! | NOT |

@if $logged-in && !$banned {
  div { data: "Welcome back!"; }
}

Conditionals

@if

Renders its body only when the condition is truthy:

$score: 85;

@if $score >= 90 {
  p { data: "Grade: A"; }
}

@if / @else

$dark: true;

@if $dark {
  body { class: "theme-dark"; }
} @else {
  body { class: "theme-light"; }
}

@if / @elif / @else

Chain multiple conditions with @elif. They are evaluated in order and only the first truthy branch is rendered:

$score: 72;

@if $score >= 90 {
  p { data: "A"; }
} @elif $score >= 80 {
  p { data: "B"; }
} @elif $score >= 70 {
  p { data: "C"; }
} @else {
  p { data: "F"; }
}

@elif chains can be arbitrarily long.

Conditionals Inside Elements

@if can appear inside a selector body to conditionally add children or attributes:

$premium: true;

div.card {
  data: "Product";
  @if $premium {
    span { data: "★ Premium"; }
  }
}

While Loop (@loop)

@loop repeats its body for as long as a condition is true. Use it like a while loop — you are responsible for mutating the variable that drives the condition, otherwise compilation will throw a runaway loop error (limit: 10,000 iterations).

$i: 1;

@loop $i <= 5 {
  p { data: $i; }
  $i: $i + 1;
}
<p>1</p>
<p>2</p>
<p>3</p>
<p>4</p>
<p>5</p>

@loop also works inside element bodies:

$count: 3;
$j: 0;

ul {
  @loop $j < $count {
    li { data: "Item $j"; }
    $j: $j + 1;
  }
}

For Loop (@for)

@for iterates over an inclusive integer range and automatically manages the iterator variable. No manual increment needed.

@for $<iterator> from <start> to <end> { ... }
@for $i from 1 to 5 {
  div { data: "Row $i"; }
}
<div>Row 1</div>
<div>Row 2</div>
<div>Row 3</div>
<div>Row 4</div>
<div>Row 5</div>

start and end can be any numeric expression, including variable references:

$start: 2;
$end: 6;

@for $n from $start to $end {
  span { data: $n; }
}

The iterator variable is scoped to each loop iteration. Combining @for with conditionals and dynamic selectors is a natural fit:

@for $i from 1 to 6 {
  div.card#$i {
    data: "Card $i";
    @if $i % 2 == 0 {
      span { data: "even"; }
    } @else {
      span { data: "odd"; }
    }
  }
}

Components

Components let you define reusable blocks of markup with named parameters, similar to template partials or functions.

Defining a Component

@component Card($title, $body) {
  div.card {
    h2 { data: $title; }
    p  { data: $body;  }
  }
}

Parameters are declared as $name inside parentheses. A component definition produces no output on its own.

Using a Component

@use Card {
  title: "Hello";
  body: "This is the card body.";
}
<div class="card">
  <h2>Hello</h2>
  <p>This is the card body.</p>
</div>

Props are matched by name and evaluated in the caller's scope, so you can pass variable values:

$heading: "Welcome";
$copy: "Some text here.";

@use Card {
  title: $heading;
  body: $copy;
}

Components in Loops

Combining @use with @for or @loop is a powerful way to render lists:

@component Item($n) {
  li { data: "Item $n"; }
}

ul {
  @for $i from 1 to 4 {
    @use Item { n: $i; }
  }
}
<ul>
  <li>Item 1</li>
  <li>Item 2</li>
  <li>Item 3</li>
  <li>Item 4</li>
</ul>

Components can be defined at the top level or inside an element body (local components).


Comments

Block Comments

/* This entire block is ignored by the compiler */
$x: 10; /* inline block comment */

Line Comments

// This line is ignored
$y: 20; // trailing line comment

Both styles are stripped at the lexer stage and produce no output.


API Reference

HTSS.run(code: string, debug: boolean): string

Runs the full compilation pipeline — lexing, parsing, transformation, and HTML generation — and returns a complete HTML document string wrapped in <!DOCTYPE html><html>...</html>.

const html = HTSS.run(`h1 { data: "Hello"; }`);

HTSS.AST(code: string, debug: boolean): BlockNode

Returns the parsed Abstract Syntax Tree without generating HTML. Useful for inspection, tooling, or writing custom transformations on top of HTSS.

const ast = HTSS.AST(`div { }`);
console.log(ast); // { type: "Block", body: [...] }

debug mode

When debug is true in either method, the following are printed to console:

  • Lexer output — the full token stream with types and values
  • Parser output — the full AST in JSON format
  • Transformer output — the resolved HTMLElementNode tree
  • Final HTML — the generated HTML string

This is useful for understanding exactly how your code is being interpreted and for troubleshooting unexpected output.


Examples

Navigation Bar

@component NavLink($label, $url) {
  li {
    a {
      href: $url;
      data: $label;
    }
  }
}

nav {
  ul {
    @use NavLink { label: "Home";    url: "/";        }
    @use NavLink { label: "About";   url: "/about";   }
    @use NavLink { label: "Contact"; url: "/contact"; }
  }
}

Conditional Theme

$light-theme: false;

body {
  @if $light-theme {
    div { class: "bg-white text-black"; data: "Light Mode"; }
  } @else {
    div { class: "bg-gray-900 text-white"; data: "Dark Mode"; }
  }
}

Error Reference

HTSS throws descriptive errors with line and column numbers.

| Error | Cause | |-------|-------| | Unterminated string at L:C | A string literal was opened with " but never closed. | | Unterminated comment at L:C | A block comment /* was opened but */ was never found. | | Unknown directive @name at L:C | An @ keyword other than if, elif, else, loop, for, component, or use was used. | | Invalid character 'x' at L:C | A character the lexer doesn't recognise was encountered. | | Unexpected '=' — did you mean '=='? | A single = was found; HTSS uses == for equality comparison. | | Undefined variable: $name | A variable was referenced before it was declared in any accessible scope. | | Unknown component: @use Name | @use was called for a component that was never defined with @component. | | Component Name: missing prop "$param" | A required parameter was not provided in the @use prop block. | | @loop exceeded 10000 iterations | The loop condition never became false. Check your increment logic. | | @for exceeded 10000 iterations | The @for range is too large (> 10,000 steps). | | @for bounds must be numbers | The from or to expression did not evaluate to a number. |


License

ISC