or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/npm-sugarss

Indent-based CSS syntax parser for PostCSS that provides an alternative to traditional CSS syntax with braces and semicolons

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/sugarss@5.0.x

To install, run

npx @tessl/cli install tessl/npm-sugarss@5.0.0

index.mddocs/

SugarSS

SugarSS is an indent-based CSS syntax parser for PostCSS that provides an alternative to traditional CSS syntax with braces and semicolons. It allows developers to write CSS using indentation similar to Sass or Stylus while maintaining full compatibility with PostCSS plugins and source maps.

Package Information

  • Package Name: sugarss
  • Package Type: npm
  • Language: JavaScript
  • Installation: npm install sugarss postcss

Core Imports

ES Modules

// Default import (contains parse and stringify)
import sugarss from "sugarss";

// Named imports
import { parse, stringify } from "sugarss";

// Direct module imports (as per package.json exports)
import parse from "sugarss/parse";
import stringify from "sugarss/stringify";
import tokenize from "sugarss/tokenize";

CommonJS

// Default require (object with parse and stringify)
const sugarss = require("sugarss");
const { parse, stringify } = sugarss;

// Destructured require
const { parse, stringify } = require("sugarss");

// Direct module requires (as per package.json exports)
const parse = require("sugarss/parse");
const stringify = require("sugarss/stringify");
const tokenize = require("sugarss/tokenize");

Basic Usage

As PostCSS Parser

import postcss from "postcss";
import sugarss from "sugarss";

const sugarssInput = `
a
  color: blue
  font-size: 16px

.nested
  .child
    padding: 10px
`;

// Parse SugarSS to CSS
const result = await postcss()
  .process(sugarssInput, { parser: sugarss });

console.log(result.css);

As Complete PostCSS Syntax

import postcss from "postcss";
import sugarss from "sugarss";

// Use as both parser and stringifier
const result = await postcss()
  .process(sugarssInput, { syntax: sugarss });

Convert CSS to SugarSS

import postcss from "postcss";
import sugarss from "sugarss";

const cssInput = `
a {
  color: blue;
  font-size: 16px;
}
`;

// Convert CSS to SugarSS format
const result = await postcss()
  .process(cssInput, { stringifier: sugarss });

console.log(result.css);
// Output:
// a
//   color: blue
//   font-size: 16px

Architecture

SugarSS is built around several key components:

  • Parser Pipeline: tokenize → liner → preprocess → parse workflow
  • PostCSS Integration: Full compatibility with PostCSS's Input, AST nodes, and plugin system
  • Syntax Features: Indentation-based nesting, multiline selectors/values, dual comment types
  • Error Handling: Detailed parse errors with precise location information
  • Type Safety: Works seamlessly with PostCSS's type system

Capabilities

Core Parser Function

Parses SugarSS syntax into PostCSS AST nodes for further processing.

/**
 * Parse SugarSS syntax into PostCSS AST
 * @param source - SugarSS source code string
 * @param opts - PostCSS Input options (from, map, etc.)
 * @returns PostCSS Root node
 */
function parse(source: string, opts?: InputOptions): Root;

interface InputOptions {
  from?: string;
  map?: SourceMapOptions | boolean;
  to?: string;
  origin?: string;
}

interface SourceMapOptions {
  inline?: boolean;
  annotation?: boolean | string;
  sourcesContent?: boolean;
  from?: string;
  to?: string;
}

interface Root {
  type: 'root';
  nodes: ChildNode[];
  source?: NodeSource;
  raws?: RootRaws;
  walkRules(callback: (rule: Rule) => void): void;
  walkDecls(callback: (decl: Declaration) => void): void;
  walkAtRules(callback: (atrule: AtRule) => void): void;
  walkComments(callback: (comment: Comment) => void): void;
}

interface NodeSource {
  input: Input;
  start?: Position;
  end?: Position;
}

interface Position {
  line: number;
  column: number;
  offset: number;
}

interface RootRaws {
  indent?: string;
  after?: string;
  semicolon?: boolean;
}

type ChildNode = Rule | AtRule | Declaration | Comment;

Usage Examples:

import { parse } from "sugarss";
import { Input } from "postcss";

// Basic parsing
const root = parse(`
a
  color: red
  font-size: 14px
`, { from: 'input.sss' });

// With source maps
const rootWithMap = parse(sugarssCode, {
  from: 'styles.sss',
  map: { inline: false }
});

// Access parsed nodes
root.walkRules(rule => {
  console.log(rule.selector); // "a"
});

root.walkDecls(decl => {
  console.log(decl.prop, decl.value); // "color", "red"
});

Core Stringifier Function

Converts PostCSS AST nodes back to SugarSS syntax format.

/**
 * Stringify PostCSS AST to SugarSS format
 * @param node - PostCSS AST node to stringify
 * @param builder - PostCSS builder function for output
 */
function stringify(node: AnyNode, builder: Builder): void;

type Builder = (str: string, node?: AnyNode, type?: 'start' | 'end') => void;

interface Rule {
  type: 'rule';
  selector: string;
  nodes: Declaration[];
  source?: NodeSource;
  raws?: RuleRaws;
}

interface AtRule {
  type: 'atrule';
  name: string;
  params: string;
  nodes?: ChildNode[];
  source?: NodeSource;
  raws?: AtRuleRaws;
}

interface Declaration {
  type: 'decl';
  prop: string;
  value: string;
  important?: boolean;
  source?: NodeSource;
  raws?: DeclRaws;
}

interface Comment {
  type: 'comment';
  text: string;
  source?: NodeSource;
  raws?: CommentRaws;
}

interface RuleRaws {
  before?: string;
  after?: string;
  selector?: RawSelector;
}

interface AtRuleRaws {
  before?: string;
  after?: string;
  afterName?: string;
  params?: RawParams;
  sssBetween?: string;
}

interface DeclRaws {
  before?: string;
  after?: string;
  between?: string;
  value?: RawValue;
}

interface CommentRaws {
  before?: string;
  after?: string;
  left?: string;
  right?: string;
}

interface RawSelector {
  value: string;
  raw: string;
}

interface RawParams {
  value: string;
  raw: string;
}

interface RawValue {
  value: string;
  raw: string;
}

type AnyNode = Root | AtRule | Rule | Declaration | Comment;

Usage Examples:

import { stringify } from "sugarss";
import postcss from "postcss";

// Use as PostCSS stringifier
const result = await postcss()
  .process(cssInput, { 
    parser: postcss.parse,
    stringifier: stringify 
  });

// Direct usage with PostCSS process
const convertToCss = await postcss()
  .process(sugarssInput, { 
    parser: parse,
    stringifier: postcss.stringify 
  });

Tokenizer Function

Low-level tokenization function that converts SugarSS source into structured tokens.

/**
 * Tokenize SugarSS source into structured tokens
 * @param input - PostCSS Input object containing source
 * @returns Array of tokens with position information
 */
function tokenize(input: Input): Token[];

interface Input {
  css: string;
  from?: string;
  origin?: string;
  error(message: string, offset: number): CssSyntaxError;
  error(message: string, line: number, column: number): CssSyntaxError;
}

type Token = [
  type: TokenType,
  value: string, 
  startOffset: number,
  endOffset: number,
  ...additional: any[]
];

type TokenType = 
  | 'newline'     // \n, \r\n, \f, \r
  | 'space'       // spaces and tabs  
  | '{'           // opening curly brace
  | '}'           // closing curly brace
  | ':'           // colon separator
  | ';'           // semicolon
  | ','           // comma
  | '('           // opening parenthesis
  | ')'           // closing parenthesis
  | 'brackets'    // matched parentheses content
  | 'string'      // quoted strings
  | 'at-word'     // @-rules like @media, @import
  | 'word'        // identifiers, values, selectors
  | 'comment';    // /* */ and // comments

interface CssSyntaxError extends Error {
  name: 'CssSyntaxError';
  message: string;
  file?: string;
  line: number;
  column: number;
  source: string;
  pos: number;
}

Usage Examples:

import { tokenize } from "sugarss/tokenize";
import { Input } from "postcss";

const input = new Input(`
a
  color: blue
`, { from: 'test.sss' });

const tokens = tokenize(input);

tokens.forEach(token => {
  const [type, value, start, end] = token;
  console.log(`${type}: "${value}" at ${start}-${end}`);
});

// Example output:
// newline: "\n" at 0-1
// word: "a" at 1-2  
// newline: "\n" at 2-3
// space: "  " at 3-5
// word: "color" at 5-10
// :: ":" at 10-11
// space: " " at 11-12
// word: "blue" at 12-16

Syntax Features

SugarSS supports comprehensive CSS syntax with indentation-based structure:

Indentation Rules

// ✅ Valid: Consistent 2-space indentation
const validIndent = `
.parent
  color: blue
  .child
    padding: 10px
`;

// ❌ Invalid: Mixed tabs and spaces
const invalidMixed = `
.parent
  color: blue  // 2 spaces
\t.child        // tab - will throw error
`;

// ❌ Invalid: First line cannot have indent  
const invalidFirst = `
  .parent  // Error: First line should not have indent
    color: blue
`;

Multiline Selectors and Values

// Multiline selectors with consistent indentation
const multilineSelector = `
.parent >
.child,
.sibling
  color: black
`;

// Multiline values with increased indentation
const multilineValue = `
.element
  background: 
    linear-gradient(rgba(0, 0, 0, 0), black)
    linear-gradient(red, rgba(255, 0, 0, 0))
  box-shadow: 1px 0 9px rgba(0, 0, 0, .4),
              1px 0 3px rgba(0, 0, 0, .6)
`;

// Continuation rules
const continuationRules = `
// 1. Brackets allow line breaks
@supports ( (display: flex) and
            (display: grid) )
  .flex-grid
    display: flex

// 2. Comma at line end continues
@media (max-width: 400px),
       (max-height: 800px)
  .responsive
    padding: 10px

// 3. Backslash before newline continues  
@media screen and \\
       (min-width: 600px)
  .desktop
    width: 100%
`;

Comment Types

const comments = `
/*
 Multiline comments
 preserved in output
 */

.element
  color: blue  // Inline comments also preserved
  
// Standalone inline comment
.another
  font-size: 16px
`;

Integration with PostCSS Ecosystem

SugarSS works seamlessly with PostCSS plugins and tools:

import postcss from "postcss";
import sugarss from "sugarss";
import autoprefixer from "autoprefixer";
import cssnano from "cssnano";

// Complete preprocessing pipeline
const result = await postcss([
  autoprefixer(),
  cssnano()
])
.process(sugarssInput, { 
  parser: sugarss,
  from: 'src/styles.sss',
  to: 'dist/styles.css'
});

// Configuration file usage (.postcssrc)
const config = {
  "parser": "sugarss",
  "plugins": {
    "postcss-simple-vars": {},
    "postcss-nested": {},
    "autoprefixer": {}
  }
};

Error Handling

SugarSS provides detailed error messages with precise location information:

SugarSS uses PostCSS's standard error system, throwing CssSyntaxError instances:

import { parse } from "sugarss";

// Indentation errors
try {
  parse(`
.parent
  color: blue
\t.child  // Mixed tabs/spaces
    padding: 10px
  `);
} catch (error) {
  console.log(error.name);     // "CssSyntaxError"
  console.log(error.message);  // "Mixed tabs and spaces are not allowed"
  console.log(`Line ${error.line}, Column ${error.column}`);
  console.log(error.pos);      // Character offset position
}

// Property syntax errors  
try {
  parse(`
.element
  color:blue  // Missing space after colon
  `);
} catch (error) {
  console.log(error.message);
  // "Unexpected separator in property"
  console.log(error.source);   // Original source code
}

// Unclosed constructs
try {
  parse(`
.element
  content: "unclosed string
  `);
} catch (error) {
  console.log(error.message);
  // "Unclosed quote"
  console.log(error.file);     // Input file path (if provided)
}

Configuration and Options

SugarSS automatically detects and adapts to different indentation styles:

// Auto-detection of indentation
const spacesInput = `
.element
  color: blue      // Detects 2-space indent
    font-size: 16px  // Nested with 4 spaces
`;

const tabsInput = `
.element
\tcolor: blue      // Detects tab indent  
\t\tfont-size: 16px  // Nested with 2 tabs
`;

// Both parse correctly with auto-detection
const spacesRoot = parse(spacesInput);
const tabsRoot = parse(tabsInput);

console.log(spacesRoot.raws.indent); // "  " (2 spaces)
console.log(tabsRoot.raws.indent);   // "\t" (tab)

Source Map Support

Full source map support for debugging and development tools:

import { parse } from "sugarss";

const result = parse(sugarssCode, {
  from: 'styles.sss',
  map: { 
    inline: false,
    annotation: true,
    sourcesContent: true
  }
});

// Source positions are preserved
result.walkDecls(decl => {
  console.log(decl.source);
  // {
  //   input: Input { css: '...', from: 'styles.sss' },
  //   start: { line: 3, column: 3, offset: 25 },
  //   end: { line: 3, column: 15, offset: 37 }
  // }
});