Rules
Rule sheets are defined using a CSS-like syntax. Rules are written using similar selectors to CSS, but the pseudo-classes and tag names are different. Properties and rules are also different.
Rules operate on the CSS Abstract Syntax Tree (AST). Each rule you write will operate on one or more nodes in the tree, just like CSS does to the DOM tree.
Selectors
Combinators
csskit rule sheets support all the combinators that CSS does:
Descendant combinator (space) - Matches any descendant node in the AST
style-rule declaration { } /* Any declaration within a style-rule */Child combinator (
>) - Matches direct children onlystyle-rule > declaration { } /* Only declarations directly in a style-rule */Next sibling combinator (
+) - Matches the immediately following siblingmedia-rule + style-rule { } /* style-rule immediately after a media-rule */Subsequent sibling combinator (
~) - Matches any following siblingmedia-rule ~ style-rule { } /* Any style-rule after a media-rule */
Type Selectors
CSS allows you to select for HTML tags like div or section, but rule sheets select for CSS AST node types instead. CSS ASTs are complicated, and so there are hundreds of different types. Here are some of the more interesting ones:
Rule types
Select different types of CSS rules:
style-rule- Regular CSS style rules (e.g.,.class { color: red; })media-rule-@mediaqueriescontainer-rule-@containerqueriessupports-rule-@supportsfeature querieslayer-rule-@layercascade layerskeyframes-rule-@keyframesanimationsfont-face-rule-@font-facecustom fontsproperty-rule-@propertycustom property definitionspage-rule-@pageprint rulescounter-style-rule-@counter-stylecustom countersnamespace-rule-@namespaceXML namespace declarationscharset-rule-@charsetcharacter encodingstarting-style-rule-@starting-styletransition start states
Example:
/* Count all media queries */
media-rule {
collect: --media-query-count;
}
/* Flag all @supports rules */
supports-rule {
level: warning;
diagnostic: "Feature query detected";
}Selector types
Select selector-related nodes:
compound-selector- A single selector sequence without combinators (e.g.,div.class#id)selector-list- A comma-separated list of selectorstag- A single tag in a selectorid- A single id in a selectorpseudo-class- A single pseudo-class in a selector
Example:
/* Flag complex compound selectors */
compound-selector:has(:nth-child(4)) {
level: warning;
diagnostic: "Complex selector - consider refactoring";
}Declarations & Values
Select CSS properties and values:
declaration- A CSS property declaration (e.g.,color: red;)style-value- Any CSS value (colors, functions, dimensions, keywords, etc.)
Example:
/* Count all declarations */
declaration {
collect: --total-properties;
}
/* Flag literal color values */
style-value[name="color"]:not(:computed) {
level: error;
diagnostic: "Use design tokens instead of literal colors";
}Attribute Selectors
Query nodes by their properties using attribute selector syntax. Attribute selectors work just like CSS, but the attributes you can select for are limited.
[name]- Has a name property[name=value]- Exact match[name^=prefix]- Starts with prefix[name$=suffix]- Ends with suffix[name*=contains]- Contains substring[name~=word]- Contains word in space-separated list[name|=lang]- Language prefix match (dash-separated)
Example:
/* Flag all background-* properties */
declaration[name^="background"] {
collect: --background-props;
}
/* Find specific animation */
keyframes-rule[name="slide"] {
level: info;
diagnostic: "Found slide animation";
}
/* Flag color properties */
declaration[name="color"] {
collect: --color-usage;
}Pseudo-Classes
Selector-Operating Pseudo-Classes
csskit rule sheets support the :not() and :has() pseudo classes just like CSS. These pseudo-classes take selectors as arguments and match based on them:
:not(<selector>)- Negation, matches nodes NOT matching the inner selector/* All non-computed color values */ style-value[name="color"]:not(:computed) { level: error; diagnostic: "Use CSS variables for colors"; }:has(<relative-selector>)- Matches elements with matching descendants/* Compound selectors containing IDs */ compound-selector:has(id) { level: error; diagnostic: "Avoid IDs in selectors"; }
Tree-Structure Pseudo-Classes
All of CSS' tree pseudo-classes are supported. These match based on a node's position in the AST:
:first-child- First child of its parent:last-child- Last child of its parent:only-child- Only child of its parent:first-of-type- First of its type among siblings:last-of-type- Last of its type among siblings:only-of-type- Only one of its type among siblings:nth-child(<An+B>)- Matches nth children (supportsAn+Bnotation)/* Every other declaration */ declaration:nth-child(2n) { collect: --even-declarations; }:nth-last-child(<An+B>)- Matches from the end:nth-of-type(<An+B>)- Matches nth of same type:nth-last-of-type(<An+B>)- Matches nth of type from end
Example:
/* Flag the first rule in a file */
style-rule:first-child {
level: info;
diagnostic: "First rule in file";
}Stateful Pseudo-Classes
These pseudo-classes check properties or characteristics of nodes:
:important- Matches!importantdeclarationsdeclaration:important { level: warning; diagnostic: "Avoid !important"; }:custom- Matches custom properties (--*)declaration:custom { collect: --custom-property-count; }:computed- Matches computed values (calc(),var(), etc.)style-value:computed { collect: --computed-values; }:shorthand- Matches shorthand propertiesdeclaration:shorthand { collect: --shorthand-usage; }:longhand- Matches longhand propertiesdeclaration:longhand { collect: --longhand-usage; }:unknown- Matches unknown/unrecognized propertiesdeclaration:unknown { level: warning; diagnostic: "Unknown property"; }:prefixed- Matches vendor-prefixed properties:prefixed { level: error; diagnostic: "Use autoprefixer instead of manual prefixes"; }:prefixed(<vendor>)- Matches specific vendor prefixes/* Only flag webkit prefixes */ :prefixed(webkit) { level: error; diagnostic: "Remove -webkit- prefix"; }Vendors:
webkit,moz,ms,o:property-type(<group>)- Matches properties in a specific group/* Flag animation properties */ declaration:property-type(animation) { collect: --animation-usage; }Available groups:
animation,background,border,color,display,flex,font,grid,layout,margin,padding,position,text,transform,transition, and more.:rule- Matches declarations within rules:at-rule- Matches at-rules:function- Matches function values:empty- Matches empty containers:root- Matches the root node:size(<comparison>)- Matches nodes by size/* Flag large selector lists */ selector-list:size(> 10) { level: warning; diagnostic: "Selector list too large"; }Operators:
>,<,>=,<=,!=
At-Rules in Rule Sheets
Rule sheets support special at-rules for defining statistics and conditional behavior.
@stat
Define statistics to collect across your CSS:
@stat --counter-name {
type: counter; /* or: bytes, lines */
}Types:
counter- Count occurrences (default)bytes- Sum byte sizeslines- Sum line counts
Example:
@stat --total-rules {
type: counter;
}
@stat --total-size {
type: bytes;
}
style-rule {
collect: --total-rules;
}
:root {
collect: --total-size;
}When you run csskit check, statistics are displayed at the end:
Statistics:
--total-rules: 42
--total-size: 15.2 KB
@when
Trigger behavior conditionally based on collected statistics:
@when (<condition>) {
message: "Custom message";
level: error; /* or: warning, advice, info */
}Conditions support comparisons: >, <, >=, <=, =, !=
Example:
declaration:important {
collect: --important-count;
}
/* Allow up to 5 !important, but error beyond that */
@when (--important-count > 5) {
message: "Too many !important declarations!";
level: error;
}
/* Warn about large files */
@when (--total-size > 100000) {
message: "CSS file exceeds 100KB";
level: warning;
}Properties in Rule Sheets
Within rule blocks, you can use these properties:
collect
Increment a stat counter for matching nodes:
declaration[name="color"] {
collect: --color-usage;
}level
Set the diagnostic severity level:
error- Fails the check (exit code 1)warning- Shows as warning (⚠)advice- Shows as suggestioninfo- Shows as information
:prefixed {
level: error;
}diagnostic
Set the message shown for matching nodes:
compound-selector:has(id) {
level: error;
diagnostic: "Avoid IDs in selectors for better specificity management";
}