Skip to content

spiracss/rel-comments

Validates parent-child links via @rel comments.

Purpose

  • Make the relationship between page entry SCSS and Blocks / child Blocks explicit
  • Stabilize automatic navigation by comment links

What it checks

  • Parent Block @rel must be placed at the top of the root scope (@use / @forward / @import can come before it; it is not allowed inside the root Block)
  • Parent Block must be the first rule in the same scope (when parent-link is required)
  • The first node under a > .child-block rule must be an @rel comment
  • The shared section is in scope; the interaction section is excluded by default
  • Optionally verifies that the path in the comment exists
  • The expected filename for child links follows fileCase (default: preserve)
  • When the @rel path includes childDir, the expected filename follows childFileCase (falls back to fileCase)
  • Allowed values: preserve | kebab | snake | camel | pascal
  • *.module.scss is also accepted for child links (useful for CSS Modules)

OK

// @rel/../components/card-list.scss
.page-home {
> .card-list {
// @rel/scss/card-list.scss
margin-top: 8px;
}
}

NG

.page-home {
// @rel/../components/card-list.scss // NG: not at the top of the root scope
> .card-list {
margin-top: 8px;
}
}
.page-home {
> .card-list {
margin-top: 8px; // NG: @rel is not the first node
}
}

fileCase example

When fileCase: 'pascal' is set, the expected file name is PascalCase. If childFileCase is set, it only applies to @rel paths that include childDir.

.page-home {
> .card-list {
// OK
// @rel/scss/CardList.scss
}
}
.page-home {
> .card-list {
// OK (CSS Modules)
// @rel/scss/card-list.module.scss
}
}

Why

  • Fixing the parent-child entry point stabilizes link resolution

Error list

missingParentRel (missing parent @rel)

Example:

// NG
.page-home {
color: #111;
}
// OK
// @rel/../components/page-home.scss
.page-home {
color: #111;
}

Reason: fix the entry point for the parent link

misplacedParentRel (invalid parent @rel placement)

Example:

// NG
.page-home {
// @rel/../components/page-home.scss
color: #111;
}
// OK
// @rel/../components/page-home.scss
.page-home {
color: #111;
}

Reason: make the parse entry point unambiguous

rootBlockNotFirst (root Block is not the first rule)

Example:

// NG
.util-reset {
box-sizing: border-box;
}
.page-home {
color: #111;
}
// OK
.page-home {
color: #111;
}
.util-reset {
box-sizing: border-box;
}

Reason: keep consistency with the parent-link entry placement

missingChildRel (missing child @rel)

Example:

// NG
.page-home {
> .card-list {
margin-top: 8px;
}
}
// OK
.page-home {
> .card-list {
// @rel/scss/card-list.scss
margin-top: 8px;
}
}

Reason: make links to child Blocks explicit

notFound (target path does not exist)

Example:

// NG
// @rel/../components/missing-block.scss
.page-home {
color: #111;
}
// OK
// @rel/../components/card-list.scss
.page-home {
color: #111;
}

Reason: keep navigation consistent

childMismatch (child Block name does not match @rel)

Example:

// NG
.page-home {
> .card-list {
// @rel/scss/hero-banner.scss
margin-top: 8px;
}
}
// OK
.page-home {
> .card-list {
// @rel/scss/card-list.scss
margin-top: 8px;
}
}

Reason: expected filenames follow fileCase / childFileCase (*.module.scss is also allowed)

selectorParseFailed (selector parse failed)

Example:

// NG
.page-home {
> : {
margin-top: 8px;
}
}
// OK
.page-home {
> .card-list {
margin-top: 8px;
}
}

Reason: unparseable selectors cannot be validated

Settings