CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-cssom

CSS Object Model implementation and CSS parser for programmatic CSS manipulation and analysis

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

lists-collections.mddocs/

Lists and Collections

Array-like collection management for media queries and document matchers with automatic deduplication and proper serialization.

Capabilities

MediaList Class

Manages collections of media types and media queries with array-like behavior and automatic deduplication.

/**
 * Collection of media types and queries for @media rules and @import conditions
 */
class MediaList {
  constructor();
  
  /** Number of media items in the list */
  length: number;
  
  /** Comma-separated string representation of all media items */
  mediaText: string;
  
  /**
   * Add a media type/query if not already present
   * @param {string} medium - Media type or query to add
   */
  appendMedium(medium): void;
  
  /**
   * Remove a media type/query if present
   * @param {string} medium - Media type or query to remove
   */
  deleteMedium(medium): void;
  
  // Array-like indexed access: [0], [1], etc.
}

Usage Examples:

const { MediaList, parse } = require("cssom");

// Create new media list
const mediaList = new MediaList();

// Add media types
mediaList.appendMedium("screen");
mediaList.appendMedium("print");
mediaList.appendMedium("(max-width: 768px)");

console.log(mediaList.length); // 3
console.log(mediaList.mediaText); // "screen, print, (max-width: 768px)"

// Array-like access
console.log(mediaList[0]); // "screen"
console.log(mediaList[1]); // "print"
console.log(mediaList[2]); // "(max-width: 768px)"

// Remove media type
mediaList.deleteMedium("print");
console.log(mediaList.length); // 2
console.log(mediaList.mediaText); // "screen, (max-width: 768px)"

// Access from parsed CSS
const sheet = parse(`
  @media screen and (max-width: 768px), print {
    body { font-size: 14px; }
  }
`);

const mediaRule = sheet.cssRules[0];
console.log(mediaRule.media.length); // 2
console.log(mediaRule.media.mediaText); // "screen and (max-width: 768px), print"

MediaList with @import Rules

MediaList integration with CSS @import rules:

const { parse } = require("cssom");

const sheet = parse(`
  @import "base.css";
  @import "responsive.css" screen and (min-width: 768px);
  @import url("print.css") print;
`);

// Access media list from import rules
const importRule1 = sheet.cssRules[0];
const importRule2 = sheet.cssRules[1];
const importRule3 = sheet.cssRules[2];

console.log(importRule1.media.length); // 0 (no media specified)
console.log(importRule2.media.mediaText); // "screen and (min-width: 768px)"
console.log(importRule3.media.mediaText); // "print"

MatcherList Class

Manages collections of document matchers for @-moz-document rules with similar array-like behavior.

/**
 * Collection of document matchers for @-moz-document rules
 */
class MatcherList {
  constructor();
  
  /** Number of matcher items in the list */
  length: number;
  
  /** Comma-separated string representation of all matchers */
  matcherText: string;
  
  /**
   * Add a document matcher if not already present
   * @param {string} matcher - Document matcher to add
   */
  appendMatcher(matcher): void;
  
  /**
   * Remove a document matcher if present
   * @param {string} matcher - Document matcher to remove
   */
  deleteMatcher(matcher): void;
  
  // Array-like indexed access: [0], [1], etc.
}

Usage Examples:

const { MatcherList, parse } = require("cssom");

// Create new matcher list
const matcherList = new MatcherList();

// Add document matchers
matcherList.appendMatcher("url(http://example.com/)");
matcherList.appendMatcher("url-prefix(https://secure.example.com/)");
matcherList.appendMatcher("domain(example.org)");

console.log(matcherList.length); // 3
console.log(matcherList.matcherText); 
// "url(http://example.com/), url-prefix(https://secure.example.com/), domain(example.org)"

// Array-like access
console.log(matcherList[0]); // "url(http://example.com/)"
console.log(matcherList[1]); // "url-prefix(https://secure.example.com/)"

// Remove matcher
matcherList.deleteMatcher("domain(example.org)");
console.log(matcherList.length); // 2

// Access from parsed CSS (Firefox-specific)
const sheet = parse(`
  @-moz-document url-prefix(http://example.com/), domain(example.org) {
    body { background: red; }
  }
`);

const docRule = sheet.cssRules[0];
console.log(docRule.matcher.length); // 2
console.log(docRule.matcher.matcherText); // "url-prefix(http://example.com/), domain(example.org)"

Advanced Collection Operations

Complex operations and manipulations with media and matcher lists:

const { MediaList, MatcherList } = require("cssom");

// Media list manipulation
const media = new MediaList();

// Bulk operations
const mediaTypes = ["screen", "print", "(max-width: 768px)", "handheld"];
mediaTypes.forEach(type => media.appendMedium(type));

console.log(media.mediaText);

// Conditional modifications
if (media.length > 2) {
  media.deleteMedium("handheld"); // Remove outdated media type
}

// Check for specific media types
for (let i = 0; i < media.length; i++) {
  if (media[i].includes("max-width")) {
    console.log("Found responsive media query:", media[i]);
  }
}

// Matcher list manipulation
const matchers = new MatcherList();

// Common document matcher patterns
const patterns = [
  "url(http://example.com/)",
  "url-prefix(https://secure.)",
  "domain(trusted.example.org)",
  "regexp(\"https://.*\\.example\\.com/.*\")"
];

patterns.forEach(pattern => matchers.appendMatcher(pattern));
console.log(matchers.matcherText);

Deduplication Behavior

Both MediaList and MatcherList automatically handle duplicates:

const { MediaList } = require("cssom");

const media = new MediaList();

// Add same media type multiple times
media.appendMedium("screen");
media.appendMedium("print");
media.appendMedium("screen"); // Duplicate - won't be added again

console.log(media.length); // 2 (not 3)
console.log(media.mediaText); // "screen, print"

// Verify deduplication
console.log(media[0]); // "screen"
console.log(media[1]); // "print"
console.log(media[2]); // undefined

Serialization and Parsing

How lists serialize to and parse from CSS text:

const { parse, MediaList } = require("cssom");

// Parse complex media queries
const complexCSS = `
  @media screen and (min-width: 768px) and (max-width: 1024px), 
         print and (orientation: landscape),
         (prefers-color-scheme: dark) {
    .content { padding: 20px; }
  }
`;

const sheet = parse(complexCSS);
const mediaRule = sheet.cssRules[0];

console.log("Parsed media count:", mediaRule.media.length);
console.log("Full media text:", mediaRule.media.mediaText);

// Individual media queries
for (let i = 0; i < mediaRule.media.length; i++) {
  console.log(`Media ${i + 1}:`, mediaRule.media[i]);
}

// Modify and re-serialize
mediaRule.media.appendMedium("(min-resolution: 2dppx)");
console.log("Updated media text:", mediaRule.media.mediaText);

Implementation Limitations

Important considerations when working with lists:

const { MatcherList } = require("cssom");

// Current limitation: URLs with commas may not parse correctly
const matchers = new MatcherList();

// This works fine
matchers.appendMatcher("url(http://example.com/path)");

// This might have issues if the URL contains commas
// matchers.appendMatcher("url(http://example.com/path?param=a,b,c)");

// Workaround: Use URL encoding or avoid commas in matcher URLs
console.log("Matcher count:", matchers.length);
console.log("Matcher text:", matchers.matcherText);

// MediaList handles complex queries better
const media = new MediaList();
media.appendMedium("screen and (min-width: 768px)");
media.appendMedium("print and (color)");

console.log("Media handling is more robust:", media.mediaText);

Collection Utilities

Utility patterns for working with CSS collections:

const { parse } = require("cssom");

// Helper function to extract all media queries from a stylesheet
function extractMediaQueries(stylesheet) {
  const mediaQueries = [];
  
  for (let i = 0; i < stylesheet.cssRules.length; i++) {
    const rule = stylesheet.cssRules[i];
    
    if (rule.type === 4) { // MEDIA_RULE
      for (let j = 0; j < rule.media.length; j++) {
        mediaQueries.push(rule.media[j]);
      }
    }
    
    if (rule.type === 3) { // IMPORT_RULE
      for (let j = 0; j < rule.media.length; j++) {
        mediaQueries.push(rule.media[j]);
      }
    }
  }
  
  return mediaQueries;
}

// Usage
const sheet = parse(`
  @import "base.css" screen;
  @media (max-width: 768px) { body { font-size: 14px; } }
  @media print { body { color: black; } }
`);

const allMediaQueries = extractMediaQueries(sheet);
console.log("All media queries found:", allMediaQueries);

Install with Tessl CLI

npx tessl i tessl/npm-cssom

docs

css-rules.md

css-values.md

index.md

lists-collections.md

parsing.md

style-declarations.md

stylesheet-management.md

tile.json