Skip to content

Configuration Options

All available options for spiracss.config.js.
Each section corresponds to a top-level key in the configuration file.

aliasRoots

Defines root paths for alias resolution.

aliasRoots: {
components: ['src/components'],
styles: ['src/styles']
}
  • The @ prefix is not required (e.g., components becomes @components/...)
  • Keys should match [a-z][a-z0-9-]* (Stylelint resolves only this pattern; Comment Links can recognize any key defined in aliasRoots)
  • Values must be arrays (relative paths recommended / absolute paths are allowed only within the project)
    • Stylelint: ignores candidates resolved outside the project root (does not validate bases)
    • Comment Links: ignores bases outside the project root and also ignores targets resolved outside the root
  • Project root basis:
    • Stylelint: cwd (running from a subdirectory changes the base path; createRulesAsync(path) only specifies the config file path and does not change the project root; run Stylelint from the project root or set the task runner working directory / Node API cwd)
    • Comment Links: the VS Code workspace folder
  • Stylelint cannot resolve aliases not defined in aliasRoots
  • Comment Links only link aliases defined in aliasRoots or the built-in defaults (@src / @components / @styles / @assets / @pages / @parts / @common)
  • CLI tools do not use aliasRoots
  • aliasRoots is required when using createRules() or createRulesAsync() in the SpiraCSS Stylelint plugin (missing it throws an error)

selectorPolicy

Shared configuration for how variants/states are represented.
Defaults to variant=data-variant / state=data-state, and aria-expanded / aria-selected / aria-disabled are also treated as state.

selectorPolicy: {
valueNaming: {
case: 'kebab',
maxWords: 2
},
variant: {
mode: 'data',
dataKeys: ['data-variant']
},
state: {
mode: 'data',
dataKey: 'data-state',
ariaKeys: ['aria-expanded', 'aria-selected', 'aria-disabled']
}
}

Settings:

FieldTypeDefaultDescription
valueNamingobject{ case: 'kebab', maxWords: 2 }Naming rules for data values (shared)
variantobject{ mode: 'data', dataKeys: ['data-variant'] }Variant representation
stateobject{ mode: 'data', dataKey: 'data-state', ariaKeys: [...] }State representation

Notes:

  • mode supports only data / class
  • Data value naming can be overridden by variant/state.valueNaming
  • HTML lint validates only the reserved keys specified here
  • If variant.mode=class and state.mode=class are both used, the HTML-to-SCSS generator treats modifiers as variants
  • aria-* values are not validated because their spec and allowed values vary widely

selectorPolicy.variant

FieldTypeDefaultDescription
mode'data' | 'class''data'Variant mode
dataKeysstring[]['data-variant']Allowlist of data attributes (empty array falls back to defaults; cannot disable)
valueNamingobject← parentNaming rules for data values

selectorPolicy.state

FieldTypeDefaultDescription
mode'data' | 'class''data'State mode
dataKeystring'data-state'Data attribute key for state (only one key allowed)
ariaKeysstring[]['aria-expanded', 'aria-selected', 'aria-disabled']Allowlist of aria attributes (empty array falls back to defaults; cannot disable)
valueNamingobject← parentNaming rules for data values

stylelint

Rule settings for the SpiraCSS Stylelint plugin.

Basics:

  • Subkeys like base / class / placement are optional (defaults apply)
  • aliasRoots is required

ESM projects:

  • Use createRulesAsync() when loading via path

Constraints:

  • Must be a plain object ([] or new Map() are invalid)

stylelint.base

Shared settings used across multiple rules.
comments / external / naming / cache / paths can be overridden per rule.

stylelint: {
base: {
comments: {
shared: /--shared/i,
interaction: /--interaction/i
},
external: {
classes: [],
prefixes: ['u-']
},
naming: {
blockCase: 'kebab',
blockMaxWords: 2,
elementCase: 'kebab',
modifierCase: 'kebab',
modifierPrefix: '-'
},
cache: {
selector: 1000,
patterns: 1000,
naming: 1000,
path: 1000
},
paths: {
childDir: 'scss',
components: ['components']
}
}
}

Settings:

FieldTypeDefaultDescription
comments.sharedRegExp / string/--shared/iShared comment matcher
comments.interactionRegExp / string/--interaction/iInteraction comment matcher
external.classesstring[][]External class exclusions (exact match)
external.prefixesstring[][]External class exclusions (prefix match)
namingobjectNaming rules (inherited by class / placement / keyframes / rel, etc.)
cache.selectornumber1000Selector parse cache
cache.patternsnumber1000Naming pattern generation cache
cache.namingnumber1000Naming pattern cache
cache.pathnumber1000@rel path existence cache
paths.childDirstring'scss'Child Block SCSS directory (default for class / rel)
paths.componentsstring[]['components']Component-layer directories (default for class)

Note: comments.shared / comments.interaction accept RegExp or string. Strings are treated as new RegExp(pattern, 'i'), and invalid/unsafe patterns fall back to defaults. Use RegExp if you need fine-grained control.

stylelint.class

Defines class naming rules and selector structure.

Settings:

FieldTypeDefaultDescription
elementDepthnumber4Max Element chain depth
childCombinatorbooleantrueRequire > for direct children of a Block
childNestingbooleantrueRequire child selectors to be nested inside the Block
rootSinglebooleantrueSingle root Block per file
rootFilebooleantrueRequire root Block name to match the filename
rootCase'preserve' | 'kebab' | 'snake' | 'camel' | 'pascal''preserve'Case for root Block filename
childFileCase'preserve' | 'kebab' | 'snake' | 'camel' | 'pascal''preserve'Case for root filename checks under childDir
childDirstring'scss'Directory for child Block SCSS
componentsDirsstring[]['components']Directories treated as the component layer
comments.sharedRegExp / string/--shared/iShared comment matcher (overrides stylelint.base.comments)
comments.interactionRegExp / string/--interaction/iInteraction comment matcher (overrides stylelint.base.comments)
external.classesstring[][]External class exclusions (exact match)
external.prefixesstring[][]External class exclusions (prefix match)

naming sub-items:

FieldTypeDefaultDescription
blockCase'kebab' | 'camel' | 'pascal' | 'snake''kebab'Block case
blockMaxWordsnumber2Max words for Block names (2–100; minimum 2 is fixed, values above 100 are clamped)
elementCase'kebab' | 'camel' | 'pascal' | 'snake''kebab'Element case
modifierCase'kebab' | 'camel' | 'pascal' | 'snake''kebab'Modifier case
modifierPrefixstring'-'Modifier prefix
customPatternsobjectundefinedCustom regex (subkeys: block / element / modifier; values are RegExp)

customPatterns subkeys:

SubkeyTargetExample
blockBlock base class/^custom-block-.+$/
elementElement base class/^icon-.+$/
modifierModifier/^--[a-z]+(-[a-z]+)?$/

Example:

stylelint: {
class: {
elementDepth: 4,
childCombinator: true,
childNesting: true,
rootSingle: true,
rootFile: true,
rootCase: 'preserve',
childFileCase: 'preserve',
childDir: 'scss',
componentsDirs: ['components'],
external: {
classes: [],
prefixes: ['swiper-']
},
naming: {
blockCase: 'kebab',
blockMaxWords: 2,
elementCase: 'kebab',
modifierCase: 'kebab',
modifierPrefix: '-',
customPatterns: {
block: /^custom-block-.+$/,
element: /^icon-.+$/,
modifier: /^--[a-z]+(-[a-z]+)?$/
}
}
}
}

Notes:

  • If you use customPatterns, verify it stays consistent with HTML placeholders (block-box / element).
  • customPatterns accept RegExp only. RegExp with g or y flags are invalid.
  • Element names are always a single word. Even with camel/pascal case, internal capitals do not count as word boundaries (bodyText / BodyText are invalid).
  • HTML lint / HTML generation also reference stylelint.base.naming / stylelint.base.external (fallbacks to stylelint.class when base is missing).
  • rootFile applies only under componentsDirs; assets/css, index.scss, and _*.scss are excluded.
  • For rootFile, files under childDir use childFileCase, while files outside childDir use rootCase.
  • For rootFile, both *.scss and *.module.scss are accepted (the .module suffix is ignored).
  • When using createRules() or createRulesAsync(), fileCase.root / fileCase.child and generator.rootFileCase / generator.childFileCase / generator.childScssDir are used as fallbacks for missing fields.

stylelint.pageLayer

Validates page-layer boundaries in page entry SCSS.

stylelint: {
pageLayer: {
enabled: true,
pageEntryAlias: 'assets',
pageEntrySubdir: 'css',
componentsDirs: ['components'],
aliasRoots: {
assets: ['src/assets'],
components: ['src/components']
}
}
}

Settings:

FieldTypeDefaultDescription
enabledbooleantrueDisables this rule when set to false (only when using createRules() / createRulesAsync())
pageEntryAliasstring'assets'Alias key for page entry root (falls back to generator.pageEntryAlias when set)
pageEntrySubdirstring'css'Page entry subdir under the alias (falls back to generator.pageEntrySubdir; empty string means alias root)
componentsDirsstring[]['components']Component-layer directories (inherits stylelint.base.paths.components)
aliasRootsobjectAlias map for page entry + link resolution (inherits spiracss.aliasRoots)
namingobjectNaming rules (inherited from stylelint.base.naming, falls back to stylelint.class.naming)
external.classesstring[]External class exclusions (merged from stylelint.base.external + stylelint.class.external)
external.prefixesstring[]External class exclusions (merged from stylelint.base.external + stylelint.class.external)
cacheobjectCache sizes (inherited from stylelint.base.cache)

If pageEntryAlias is missing from aliasRoots (or resolves to no directories), the rule is skipped.

stylelint.placement

Validates property placement (container / item / internal).

stylelint: {
placement: {
elementDepth: 4,
marginSide: 'top',
position: true,
sizeInternal: true,
responsiveMixins: []
}
}

Settings:

FieldTypeDefaultDescription
elementDepthnumber4Max Element chain depth (when using createRules() / createRulesAsync(), falls back to class.elementDepth if unset)
marginSide'top' | 'bottom''top'Allowed vertical margin side
positionbooleantrueEnable child Block position restrictions
sizeInternalbooleantrueTreat width/height/min/max as internal properties
responsiveMixinsstring[][]@include mixins treated as transparent
comments.sharedRegExp / string/--shared/iShared comment matcher (overrides stylelint.base.comments)
comments.interactionRegExp / string/--interaction/iInteraction comment matcher (overrides stylelint.base.comments)

stylelint.interactionScope

Validates placement rules for the interaction section (// --interaction and @at-root & { ... }).

stylelint: {
interactionScope: {
pseudos: [':hover', ':focus', ':focus-visible', ':active', ':visited'],
requireAtRoot: true,
requireComment: true,
requireTail: true,
commentOnly: false
}
}

The interaction section must always be placed directly under the root Block (wrapper at-rules like @layer/@supports/@media/@container/@scope are allowed).

Settings:

FieldTypeDefaultDescription
pseudosstring[][':hover', ':focus', ':focus-visible', ':active', ':visited']Pseudo-classes to check
requireAtRootbooleantrueRequire @at-root & { ... } and selectors that start with &
requireCommentbooleantrueRequire // --interaction
requireTailbooleantrueRequire the interaction block to be at the end
commentOnlybooleanfalseOnly validate blocks with the comment
comments.sharedRegExp / string/--shared/iShared comment matcher (overrides stylelint.base.comments)
comments.interactionRegExp / string/--interaction/iInteraction comment matcher (overrides stylelint.base.comments)

stylelint.interactionProps

Validates transition/animation placement and transition target properties in the interaction section.

stylelint: {
interactionProps: {
// override section comment patterns if needed
// comments: { shared: /--shared/i, interaction: /--interaction/i }
}
}

Settings:

FieldTypeDefaultDescription
comments.sharedRegExp / string/--shared/iShared comment matcher (overrides stylelint.base.comments)
comments.interactionRegExp / string/--interaction/iInteraction comment matcher (overrides stylelint.base.comments)
namingobjectNaming rules (inherited from stylelint.base.naming, falls back to stylelint.class.naming)
external.classesstring[]External class exclusions (merged from stylelint.base.external + stylelint.class.external)
external.prefixesstring[]External class exclusions (merged from stylelint.base.external + stylelint.class.external)
cacheobjectCache sizes (inherited from stylelint.base.cache)

stylelint.keyframes

Validates @keyframes naming and placement rules.

stylelint: {
keyframes: {
enabled: true,
actionMaxWords: 3,
blockSource: 'selector',
blockWarnMissing: true,
sharedPrefixes: ['kf-'],
sharedFiles: ['keyframes.scss'],
ignoreFiles: [],
ignorePatterns: [],
// Skip placement checks for keyframes matched by ignorePatterns (default: false)
ignoreSkipPlacement: false
}
}

Settings:

FieldTypeDefaultDescription
enabledbooleantrueDisables this rule when set to false (only when using createRules() / createRulesAsync())
actionMaxWordsnumber3Max words in the action segment (1–3)
blockSource'selector' | 'file' | 'selector-or-file''selector'Block name source (root selector, file name, or fallback)
blockWarnMissingbooleantrueWarn when the root Block cannot be resolved
sharedPrefixesstring[]['kf-']Prefixes for shared keyframes
sharedFiles(string | RegExp)[]['keyframes.scss']File patterns allowed for shared keyframes (strings are suffix matches)
ignoreFiles(string | RegExp)[][]File patterns to skip this rule (strings are suffix matches)
ignorePatterns(string | RegExp)[][]Keyframes names to ignore (strings are treated as RegExp patterns)
ignoreSkipPlacementbooleanfalseSkip placement checks (root/end) for keyframes matched by ignorePatterns

stylelint.pseudo

Requires pseudo-classes/elements to be nested under &.

stylelint: {
pseudo: {
enabled: true
}
}
  • Allowed: .btn { &:hover { ... } }, .btn { &::before { ... } }
  • Not allowed: .btn:hover { ... }, & > .btn:hover { ... }

Settings:

FieldTypeDefaultDescription
enabledbooleantrueDisables this rule when set to false (only when using createRules() / createRulesAsync())

stylelint.rel

Defines rules for @rel link comments.

stylelint: {
rel: {
requireScss: true,
requireMeta: true,
requireParent: true,
requireChild: true,
requireChildShared: true,
requireChildInteraction: false,
validatePath: true,
skipNoRules: true,
childDir: 'scss',
fileCase: 'preserve'
}
}

Settings:

FieldTypeDefaultDescription
requireScssbooleantrueRequire @rel in SCSS under childDir
requireMetabooleantrueRequire a top-of-file comment when @include meta.load-css("<childDir>") is present
requireParentbooleantrueRequire child-to-parent @rel (only when requireMeta / requireScss is enabled)
requireChildbooleantrueRequire parent-to-child @rel
requireChildSharedbooleantrueRequire child @rel in shared sections
requireChildInteractionbooleanfalseRequire child @rel in interaction sections
validatePathbooleantrueValidate path existence
skipNoRulesbooleantrueSkip SCSS files without selector rules
childDirstring'scss'Directory name for child Block SCSS (falls back to generator.childScssDir when using createRules() or createRulesAsync())
fileCase'preserve' | 'kebab' | 'snake' | 'camel' | 'pascal''preserve'Expected file name case for child @rel comments (non-childDir targets)
childFileCase'preserve' | 'kebab' | 'snake' | 'camel' | 'pascal'undefinedExpected file name case when the @rel path includes childDir (falls back to fileCase when omitted)
aliasRootsobject← top-levelAlias roots for @rel resolution
comments.sharedRegExp / string/--shared/iShared comment matcher (overrides stylelint.base.comments)
comments.interactionRegExp / string/--interaction/iInteraction comment matcher (overrides stylelint.base.comments)

Notes:

  • Alias resolution uses aliasRoots (when validatePath: true)
  • requireParent only fires when requireMeta is enabled and the parent Block includes @include meta.load-css(...), or when requireScss is enabled and the SCSS file lives under childDir
  • fileCase / childFileCase accept *.module.scss (CSS Modules) in addition to .scss

See Comment Links for details.

htmlFormat

Controls the output attribute when adding HTML placeholders.

htmlFormat.classAttribute

Settings:

FieldTypeDefaultDescription
classAttribute'class' | 'className''class'Class attribute to output

Notes:

  • Input class / className is normalized to this setting
  • Internally, class / className are temporarily converted to data-spiracss-classname and restored on output
  • No file extension auto-detection is performed

jsxClassBindings

Controls how JSX class / className bindings are interpreted (HTML CLI / VS Code).

jsxClassBindings.memberAccessAllowlist

Settings:

FieldTypeDefaultDescription
memberAccessAllowliststring[]undefinedAllowlist of base identifiers for member access (e.g. styles.foo). When set, only these bases are treated as class sources.

Notes:

  • When omitted, all member access is allowed
  • An empty array disables member access extraction
  • Applies to HTML-to-SCSS generation and placeholder insertion

generator

Configuration for HTML-to-SCSS conversion.

Settings:

FieldTypeDefaultDescription
globalScssModulestring'@styles/partials/global'Inserts @use "<value>" as *; at the top of each generated SCSS file (in root mode, the root Block SCSS also includes @use "sass:meta"; immediately after it)
pageEntryAliasstring'assets'Alias for the page entry comment
pageEntrySubdirstring'css'Subdirectory for the page entry comment
rootFileCase'preserve' | 'kebab' | 'snake' | 'camel' | 'pascal''preserve'Case for the root Block filename
childFileCase'preserve' | 'kebab' | 'snake' | 'camel' | 'pascal''preserve'Case for child Block filenames
childScssDirstring'scss'Output directory for child Block SCSS
layoutMixinsstring[][]Array of layout mixins

Example:

generator: {
globalScssModule: '@styles/global',
pageEntryAlias: 'assets',
pageEntrySubdir: 'css',
rootFileCase: 'kebab',
childFileCase: 'kebab',
childScssDir: 'scss',
layoutMixins: ['@include breakpoint-up(md)', '@include breakpoint-up(lg)']
}

Notes:

  • pageEntryAlias / pageEntrySubdir: in root mode, outputs a comment like // @assets/css/index.scss at the top of the root Block SCSS file (in selection mode, // @rel/(parent-block).scss is output instead)
  • pageEntrySubdir: when empty, the subdirectory is omitted (e.g. // @assets/index.scss)
  • rootFileCase: does not apply to child blocks
  • childFileCase: used for child Block filenames and @rel comments
  • layoutMixins: generates one @include block per entry (default: disabled)