<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: zetsubo</title>
    <description>The latest articles on DEV Community by zetsubo (@zetsubo).</description>
    <link>https://dev.to/zetsubo</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3738578%2F8076368c-8af9-41f3-8f7e-130aaa4dc955.png</url>
      <title>DEV Community: zetsubo</title>
      <link>https://dev.to/zetsubo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/zetsubo"/>
    <language>en</language>
    <item>
      <title>Same Lint, Same Result — A Stylelint Toolchain for Humans and AI Agents</title>
      <dc:creator>zetsubo</dc:creator>
      <pubDate>Sat, 14 Feb 2026 00:31:11 +0000</pubDate>
      <link>https://dev.to/zetsubo/same-lint-same-result-a-stylelint-toolchain-for-humans-and-ai-agents-k02</link>
      <guid>https://dev.to/zetsubo/same-lint-same-result-a-stylelint-toolchain-for-humans-and-ai-agents-k02</guid>
      <description>&lt;p&gt;Part 4 laid out SpiraCSS's core principles and structure. This final part covers the tools and procedures for adopting it in practice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tool Overview
&lt;/h2&gt;

&lt;p&gt;SpiraCSS is both a design methodology and a toolchain that supports it.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Users&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Stylelint plugin&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Automatically verifies SCSS structure, naming, and placement&lt;/td&gt;
&lt;td&gt;Humans and AI agents&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;HTML CLI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Generates SCSS from HTML, validates HTML structure&lt;/td&gt;
&lt;td&gt;Primarily AI agents&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AI Agent Guide&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Self-contained document covering rule specs, decision flow, and fix procedures&lt;/td&gt;
&lt;td&gt;AI agents&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;VS Code: Comment Links&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Click &lt;code&gt;@rel&lt;/code&gt; comments to navigate between files&lt;/td&gt;
&lt;td&gt;Humans&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;VS Code: HTML to SCSS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Generate SCSS from HTML via keyboard shortcuts&lt;/td&gt;
&lt;td&gt;Humans&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Humans use VS Code; AI agents use the CLI. Both share the same generation logic and the same validation rules.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stylelint Plugin — The Design Guardian
&lt;/h2&gt;

&lt;p&gt;This plugin is the core of SpiraCSS. Eight rules systematically verify the design principles described in Part 4.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rule&lt;/th&gt;
&lt;th&gt;Verification&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;spiracss/class-structure&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Block/Element naming, parent-child structure, section composition&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;spiracss/property-placement&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Container/Item/Internal property placement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;spiracss/page-layer&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Page layer boundaries and links&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;spiracss/interaction-scope&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;--interaction section position and structure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;spiracss/interaction-properties&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;transition/animation placement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;spiracss/keyframes-naming&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;@keyframes naming and placement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;spiracss/pseudo-nesting&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pseudo-class and pseudo-element nesting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;spiracss/rel-comments&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;a class="mentioned-user" href="https://dev.to/rel"&gt;@rel&lt;/a&gt; comment parent-child links&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Setup
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn add &lt;span class="nt"&gt;-D&lt;/span&gt; @spiracss/stylelint-plugin stylelint stylelint-scss postcss-scss
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// stylelint.config.js (ESM)&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;spiracss&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createRules&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@spiracss/stylelint-plugin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;spiracssConfig&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./spiracss.config.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;spiracss&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;stylelint-scss&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;customSyntax&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;postcss-scss&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;ignoreFiles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src/styles/partials/**/*.scss&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;!src/styles/partials/keyframes.scss&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nf"&gt;createRules&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;spiracssConfig&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;scss/at-rule-no-unknown&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;createRules()&lt;/code&gt; reads the &lt;code&gt;spiracss.config.js&lt;/code&gt; settings and distributes appropriate options to all eight rules. Individual rules can also be disabled.&lt;/p&gt;

&lt;h3&gt;
  
  
  Error Message Examples
&lt;/h3&gt;

&lt;p&gt;Here are simplified examples showing the "feedback-style" error messages discussed in Part 3. Actual output includes additional context and configuration hints.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Structure violation:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Element ".title" cannot contain Block ".nav-links".
Only Blocks can contain other Blocks.
Move ".nav-links" to be a direct child of a Block instead.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Naming violation:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Block names must use two-word kebab-case (e.g., "hero-container").
Single-word names are reserved for Elements.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Section placement violation:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;":hover" must be inside the --interaction section.
Use "@at-root &amp;amp;" to create the interaction scope.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every error explains what's wrong and how to fix it. AI agents parse these messages and autonomously correct the code. Humans can also understand the fix the first time they encounter a rule.&lt;/p&gt;

&lt;h2&gt;
  
  
  HTML CLI — A Generation Tool for AI Agents
&lt;/h2&gt;

&lt;p&gt;The HTML CLI is designed for AI agents and automation scripts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn add &lt;span class="nt"&gt;-D&lt;/span&gt; @spiracss/html-cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It provides three commands:&lt;/p&gt;

&lt;h3&gt;
  
  
  SCSS Generation (&lt;code&gt;spiracss-html-to-scss&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Generates SpiraCSS-compliant SCSS scaffolding from HTML.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;component.html | yarn spiracss-html-to-scss &lt;span class="nt"&gt;--root&lt;/span&gt; &lt;span class="nt"&gt;--stdin&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--base-dir&lt;/span&gt; src/components/feature-card
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;feature-card/
├── feature-card.scss
└── scss/
    ├── card-header.scss
    ├── card-body.scss
    └── index.scss
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AI agent workflow: Load the AI Agent Guide (a self-contained document covering rule specs, decision flow, and fix procedures) into context → Write HTML → Generate SCSS via CLI → Add styles to generated SCSS → Validate with Stylelint. The document provides structural understanding; lint verifies the result — this two-layer approach supports autonomous AI correction.&lt;/p&gt;

&lt;p&gt;To be clear: &lt;strong&gt;lint alone is enough to guarantee convergence.&lt;/strong&gt; Even without the AI Agent Guide, the feedback loop described in Part 3 — write, lint, fix, re-lint — will converge to architecture-compliant code. The Guide accelerates the process by reducing the number of iterations, but it's supplementary, not required. The AI Agent Guide is included in each &lt;a href="https://spiracss.jp/introduction/starters/" rel="noopener noreferrer"&gt;starter project&lt;/a&gt; and available on the &lt;a href="https://spiracss.jp" rel="noopener noreferrer"&gt;SpiraCSS site&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  HTML Structure Validation (&lt;code&gt;spiracss-html-lint&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Detects SpiraCSS structural rule violations at the HTML stage.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;component.html | yarn spiracss-html-lint &lt;span class="nt"&gt;--root&lt;/span&gt; &lt;span class="nt"&gt;--stdin&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Catches issues like Elements acting as parents of Blocks or naming rule violations before any SCSS is written.&lt;/p&gt;

&lt;h3&gt;
  
  
  Placeholder Insertion (&lt;code&gt;spiracss-html-format&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Assigns placeholder classes to HTML elements that don't have classes yet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;raw.html | yarn spiracss-html-format &lt;span class="nt"&gt;--stdin&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When creating a new component, the workflow — write HTML, insert placeholders, generate SCSS — can be fully automated.&lt;/p&gt;

&lt;h2&gt;
  
  
  VS Code Extensions — Productivity Tools for Humans
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Comment Links
&lt;/h3&gt;

&lt;p&gt;Navigate between files by Cmd/Ctrl+clicking &lt;code&gt;@rel&lt;/code&gt; comments. With one Block per file, jumping between parent and child Blocks is common. This extension eliminates that friction.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="nc"&gt;.feature-card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;.card-header&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// @rel/scss/card-header.scss   ← Cmd+Click to navigate&lt;/span&gt;
    &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Supports aliases (&lt;code&gt;@components/...&lt;/code&gt;, &lt;code&gt;@styles/...&lt;/code&gt;), enabling one-click navigation from page SCSS to components as well.&lt;/p&gt;

&lt;h3&gt;
  
  
  HTML to SCSS
&lt;/h3&gt;

&lt;p&gt;Generate SCSS via keyboard shortcuts in VS Code. Uses the same generation logic as the CLI, delivered through a human-friendly UI.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Shortcut&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Generate from Root&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Cmd+Ctrl+A&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Generate SCSS for the root Block and child Blocks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Generate from Selection&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Cmd+Ctrl+S&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Generate SCSS for the selected Block only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Insert Placeholders&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Cmd+Ctrl+D&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Assign placeholder classes to elements without classes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Shared Configuration (&lt;code&gt;spiracss.config.js&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;A configuration file referenced by all tools. Place it in the project root.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;aliasRoots&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src/components&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;common&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src/components/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;pages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src/components/pages&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src/components/parts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src/styles&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;aliasRoots&lt;/code&gt; is used for file path resolution. Comment Links aliases, Stylelint &lt;code&gt;@rel&lt;/code&gt; validation, CLI output destinations — all share this configuration.&lt;/p&gt;

&lt;p&gt;Variant/State syntax (data attribute mode / class mode) and Element naming case conventions are also configurable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;The minimal setup is three steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Place &lt;code&gt;spiracss.config.js&lt;/code&gt; in the project root&lt;/li&gt;
&lt;li&gt;Install the Stylelint plugin and configure with &lt;code&gt;createRules()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;yarn stylelint "src/**/*.scss"&lt;/code&gt; — then fix issues following the error messages&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's all it takes to get SpiraCSS structural verification running. VS Code extensions (Comment Links / HTML to SCSS) and the HTML CLI can be added as needed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup Notes
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Node.js 20.19.0 or higher&lt;/strong&gt; required (for the Stylelint plugin; the HTML CLI requires Node.js 20+)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stylelint v17+&lt;/strong&gt; required (for v16, use &lt;code&gt;@spiracss/stylelint-plugin@0.3.x&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;All tools are currently in &lt;strong&gt;Beta&lt;/strong&gt;. Feedback is welcome on &lt;a href="https://github.com/zetsubo-dev/spiracss/issues" rel="noopener noreferrer"&gt;GitHub Issues&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ESM format required&lt;/strong&gt; for stylelint.config.js (&lt;code&gt;export default&lt;/code&gt;) when using this import style. Use &lt;code&gt;.mjs&lt;/code&gt; extension or set &lt;code&gt;"type": "module"&lt;/code&gt; in package.json&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exclude global-layer SCSS&lt;/strong&gt;: Set &lt;code&gt;ignoreFiles&lt;/code&gt; to &lt;code&gt;src/styles/partials/**/*.scss&lt;/code&gt; (don't exclude &lt;code&gt;keyframes.scss&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Gradual adoption in existing projects is supported. Disable individual rules with &lt;code&gt;null&lt;/code&gt; and expand coverage incrementally&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Series Summary
&lt;/h2&gt;

&lt;p&gt;This series discussed CSS architecture for the AI coding agent era in five parts.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Part 1&lt;/strong&gt;: CSS architecture should shift from "rules to memorize" to "feedback systems"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 2&lt;/strong&gt;: Preventing design drift requires "invariants," not conventions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 3&lt;/strong&gt;: Leveraging invariants requires a feedback loop that returns "what to fix and how"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 4&lt;/strong&gt;: SpiraCSS derives everything from a single principle: "parent handles layout, child handles internals"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 5&lt;/strong&gt;: A Stylelint plugin, CLI, and VS Code extensions support this design in practice&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The underlying idea is a system where the design converges to the same result regardless of who writes it. Whether it's a junior developer, a senior developer, or an AI agent — if it passes the same lint, at minimum, the structure, separation of responsibilities, and placement align to the same standard.&lt;/p&gt;

&lt;p&gt;The challenge of CSS architecture has long focused on "how to create good rules and how to enforce them." But in an era where AI participates as an author, the "enforce rules" model itself needs rethinking.&lt;/p&gt;

&lt;p&gt;What matters is not creating good rules, but &lt;strong&gt;designing an environment that resists breaking down.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;This series comes down to one idea: in the AI era, CSS architecture should focus not on memorizing conventions, but on designing systems that converge to the same result.&lt;/p&gt;

&lt;p&gt;SpiraCSS is one implementation of that idea, but the underlying approach applies more broadly. Make the structure machine-verifiable, and return feedback that both humans and AI can act on. Meet those conditions, and the design stays stable through real-world use.&lt;/p&gt;

&lt;p&gt;If this perspective resonates, try applying it to just one existing component and see how it changes your review process.&lt;/p&gt;




&lt;p&gt;SpiraCSS's design specs, tools, and source code are all open source.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SpiraCSS: &lt;a href="https://spiracss.jp" rel="noopener noreferrer"&gt;https://spiracss.jp&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/zetsubo-dev/spiracss" rel="noopener noreferrer"&gt;https://github.com/zetsubo-dev/spiracss&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>css</category>
      <category>webdev</category>
      <category>ai</category>
      <category>frontend</category>
    </item>
    <item>
      <title>The CSS Fundamental You Won't Learn from BEM/SMACSS Alone — Property Responsibility</title>
      <dc:creator>zetsubo</dc:creator>
      <pubDate>Tue, 10 Feb 2026 10:22:52 +0000</pubDate>
      <link>https://dev.to/zetsubo/the-css-fundamental-you-wont-learn-from-bemsmacss-alone-property-responsibility-5ahf</link>
      <guid>https://dev.to/zetsubo/the-css-fundamental-you-wont-learn-from-bemsmacss-alone-property-responsibility-5ahf</guid>
      <description>&lt;p&gt;You've learned BEM. Your naming is correct. Your components are properly decomposed.&lt;br&gt;
You think you "get" CSS, right?&lt;/p&gt;

&lt;p&gt;Then answer this — should that CSS property go on the parent (the container) or the child (the item)? Can you explain why?&lt;/p&gt;

&lt;p&gt;If you can't, what's missing isn't a naming convention. It's &lt;strong&gt;the fundamentals of CSS itself&lt;/strong&gt; — something that comes before any design methodology.&lt;/p&gt;

&lt;p&gt;This article lays out that foundation: the concept of property responsibility.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why Correct Naming Still Leads to Broken Layouts
&lt;/h2&gt;

&lt;p&gt;Look at these two patterns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Pattern A&lt;/span&gt;
&lt;span class="nc"&gt;.card-list&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;flex-direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.card&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nc"&gt;.card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;margin-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;24px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Pattern B&lt;/span&gt;
&lt;span class="nc"&gt;.card-list&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;flex-direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Parent decides spacing between children&lt;/span&gt;
  &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;.card&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nc"&gt;.card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;margin-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;24px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;.card-list&lt;/code&gt; is the parent (the container) and &lt;code&gt;.card&lt;/code&gt; is the child (the item).&lt;/p&gt;

&lt;p&gt;Assuming &lt;code&gt;.card&lt;/code&gt; elements are direct children, both produce the same visual result.&lt;/p&gt;

&lt;p&gt;But in Pattern A, &lt;code&gt;.card&lt;/code&gt; knows about its own layout position (&lt;code&gt;margin-top&lt;/code&gt;). Change the parent, and it breaks.&lt;/p&gt;

&lt;p&gt;Pattern B separates responsibilities — placement (&lt;code&gt;margin-top&lt;/code&gt;) is handled by the parent, internal styling (&lt;code&gt;padding&lt;/code&gt;) by the child.&lt;/p&gt;

&lt;p&gt;This difference has nothing to do with naming conventions. It's about understanding CSS's layout model itself.&lt;/p&gt;

&lt;p&gt;In Flexbox, properties are split between Container (&lt;code&gt;display: flex&lt;/code&gt;, etc.) and Item (&lt;code&gt;align-self&lt;/code&gt;, &lt;code&gt;flex&lt;/code&gt;, etc.). Since &lt;code&gt;margin&lt;/code&gt; determines the outer spacing of a child element, specifying it via the parent's child selector tends to be more consistent in practice.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem with Writing CSS That "Looks Right"
&lt;/h2&gt;

&lt;p&gt;Here's a pattern I see all the time. BEM naming is correct, components are reasonably decomposed — but property placement is all over the place:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Some components write &lt;code&gt;margin&lt;/code&gt; on themselves; others have it specified from the parent&lt;/li&gt;
&lt;li&gt;Container and Item properties are mixed in the same block without distinguishing roles
(An element serving as both Container and Item is perfectly normal — mixing them &lt;em&gt;unconsciously&lt;/em&gt; is what makes code unreadable)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;padding&lt;/code&gt; and &lt;code&gt;margin&lt;/code&gt; are jumbled together with no apparent logic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is what happens when you write without understanding property responsibility. The output looks correct. It passes code review. But change the parent component and the layout breaks.&lt;/p&gt;

&lt;p&gt;This isn't an individual skill issue. &lt;strong&gt;It's a structural problem: CSS education is too heavily weighted toward naming and component decomposition.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You perfect your TypeScript types. You learn React patterns. But are you still placing CSS properties based on gut feeling?&lt;/p&gt;

&lt;p&gt;Even when AI coding agents write CSS, the same problem occurs. Naming and component decomposition may look reasonable, but property placement tends to be inconsistent. When the human-side criteria for CSS design are ambiguous to begin with, this is likely to happen.&lt;/p&gt;

&lt;p&gt;That's the fundamental both humans and AI struggle with: property responsibility.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Three Property Categories
&lt;/h2&gt;

&lt;p&gt;CSS properties can be classified into three categories by responsibility:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Examples&lt;/th&gt;
&lt;th&gt;Where to Write&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Container&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Determines how children are arranged&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;display&lt;/code&gt;, &lt;code&gt;gap&lt;/code&gt;, &lt;code&gt;justify-content&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;On the element itself&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Item&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Position within parent's layout&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;margin&lt;/code&gt;, &lt;code&gt;flex&lt;/code&gt;, &lt;code&gt;order&lt;/code&gt;, &lt;code&gt;align-self&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;In the parent's child selector (e.g., &lt;code&gt;&amp;gt; .child&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Internal&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The element's own appearance&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;padding&lt;/code&gt;, &lt;code&gt;font-size&lt;/code&gt;, &lt;code&gt;color&lt;/code&gt;, &lt;code&gt;background&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;On the element itself&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This classification reflects how CSS layout models (Flexbox, Grid) actually work. It's not specialized framework knowledge — it's CSS fundamentals (this article is an overview of the design principle; the lint rules mentioned later cover a subset of this).&lt;/p&gt;

&lt;p&gt;Note that Item properties (&lt;code&gt;align-self&lt;/code&gt;, &lt;code&gt;flex&lt;/code&gt;, etc.) are applied to child elements per the CSS spec, but &lt;strong&gt;the key point of this approach is that the parent specifies them on the child&lt;/strong&gt; (via &lt;code&gt;&amp;gt; .child&lt;/code&gt; selectors). At boundaries where the parent can't directly select the child (slots, dynamic insertion, etc.), use a wrapper (e.g., &lt;code&gt;.card-list__item&lt;/code&gt;) or utility class (e.g., &lt;code&gt;.u-mt-24&lt;/code&gt;) to bridge the gap.&lt;/p&gt;

&lt;p&gt;Just being aware of these three categories changes code structure significantly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Container properties → on the element itself&lt;/span&gt;
&lt;span class="nc"&gt;.card-list&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;flex-wrap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="na"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;24px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Item properties → from parent to child&lt;/span&gt;
  &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;.feature-card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="m"&gt;300px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;align-self&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex-start&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Internal properties → on the element itself&lt;/span&gt;
&lt;span class="nc"&gt;.feature-card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you can see this classification, you can tell whether property placement in someone else's code is intentional:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;margin&lt;/code&gt; specified from the parent's child selector → understands responsibility&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;margin&lt;/code&gt; written on the child component itself → responsibility is unclear&lt;/li&gt;
&lt;li&gt;Container and Item properties are separated → understands CSS layout models&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;display: flex&lt;/code&gt; and &lt;code&gt;margin-top&lt;/code&gt; jumbled in the same selector → no distinction
(Intentional dual-role cases are a different matter)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Writing &lt;code&gt;margin&lt;/code&gt; with intent vs. gut feeling fundamentally changes how fragile your code is. Once you internalize this foundation and revisit your past code, you'll likely notice you were "matching the visual result without understanding the structure."&lt;/p&gt;

&lt;h2&gt;
  
  
  From "Memorize" to "Learn Through Feedback"
&lt;/h2&gt;

&lt;p&gt;"Be mindful of property responsibility" is easy to say, but maintaining team-wide quality through awareness alone is difficult. This is the same problem as naming conventions — the limits of the "memorize and follow" model.&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://spiracss.jp" rel="noopener noreferrer"&gt;SpiraCSS&lt;/a&gt;, which I develop, this approach is built into Stylelint rules. Write &lt;code&gt;margin-top&lt;/code&gt; on a child component, and lint prompts you to specify it from the parent side. Whether a human writes it or AI generates it, you get the same feedback.&lt;/p&gt;

&lt;p&gt;I've written in more detail about this shift from "memorize rules" to "converge through feedback" in a separate series:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/zetsubo/series/35421"&gt;CSS Architecture in the AI Agent Era&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Naming organizes appearance. Responsibility organizes structure.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Next time you write CSS, check just one thing — "Is this property the parent's responsibility, or the child's?" That alone changes how your code breaks.&lt;/p&gt;

</description>
      <category>css</category>
      <category>webdev</category>
      <category>ai</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Parent Owns Layout — A New CSS Architecture for the AI Era — Drift-Resistant by Design</title>
      <dc:creator>zetsubo</dc:creator>
      <pubDate>Mon, 09 Feb 2026 09:51:24 +0000</pubDate>
      <link>https://dev.to/zetsubo/parent-owns-layout-a-new-css-architecture-for-the-ai-era-drift-resistant-by-design-b62</link>
      <guid>https://dev.to/zetsubo/parent-owns-layout-a-new-css-architecture-for-the-ai-era-drift-resistant-by-design-b62</guid>
      <description>&lt;p&gt;Parts 2–3 established the framework of "invariants" and "feedback loops." Part 3 focused on feedback message design; this part defines the concrete rule system those messages enforce — the principles and structure of &lt;a href="https://spiracss.jp" rel="noopener noreferrer"&gt;SpiraCSS&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Single Principle: Parents Handle Layout, Children Handle Only Internals
&lt;/h2&gt;

&lt;p&gt;Every rule in SpiraCSS derives from one principle:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The parent decides the child's layout; the child only writes its own internals.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;CSS layout is fundamentally a "parent arranges children" mechanism. You set &lt;code&gt;display: flex&lt;/code&gt; on the parent and &lt;code&gt;align-self&lt;/code&gt; on the child. Since a child's &lt;code&gt;margin&lt;/code&gt; also affects the parent's layout, it should be controlled by the parent.&lt;/p&gt;

&lt;p&gt;All structural rules are derived from this principle.&lt;/p&gt;

&lt;h2&gt;
  
  
  Block and Element — Two Structural Units
&lt;/h2&gt;

&lt;p&gt;SpiraCSS classifies all classes into &lt;strong&gt;Blocks&lt;/strong&gt; and &lt;strong&gt;Elements&lt;/strong&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Naming Convention&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Block&lt;/td&gt;
&lt;td&gt;Two-word kebab-case&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.feature-card&lt;/code&gt;, &lt;code&gt;.card-header&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Independent component unit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Element&lt;/td&gt;
&lt;td&gt;Single-word&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.title&lt;/code&gt;, &lt;code&gt;.body&lt;/code&gt;, &lt;code&gt;.icon&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Constituent part within a Block&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This naming convention corresponds to BEM's "Block__Element," but with a crucial difference: &lt;strong&gt;the naming pattern itself expresses the structure.&lt;/strong&gt; Two words mean Block, one word means Element — no room for interpretation. (This naming rule is the default; case conventions can be customized in &lt;code&gt;spiracss.config.js&lt;/code&gt;.)&lt;/p&gt;

&lt;p&gt;The allowed parent-child relationships are also explicit:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parent&lt;/th&gt;
&lt;th&gt;Child&lt;/th&gt;
&lt;th&gt;Allowed&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Block&lt;/td&gt;
&lt;td&gt;Block&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Block&lt;/td&gt;
&lt;td&gt;Element&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Element&lt;/td&gt;
&lt;td&gt;Block&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Element&lt;/td&gt;
&lt;td&gt;Element&lt;/td&gt;
&lt;td&gt;✅ (with depth limit)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Placing a Block under an Element is forbidden. Element-to-Element nesting is allowed but has a depth ceiling (default 4 levels, configurable via &lt;code&gt;elementDepth&lt;/code&gt;). The fundamental structure is "Blocks own children." This doesn't restrict DOM nesting itself; it constrains only responsibility-bearing class relationships. This constraint automatically determines component boundaries.&lt;/p&gt;

&lt;p&gt;"What if Element nesting gets too deep?" The answer is straightforward: if a unit has independent responsibility, promote it to a Block (e.g., &lt;code&gt;.title&lt;/code&gt; → &lt;code&gt;.title-box&lt;/code&gt;). Decorative elements are handled with tag selectors or pseudo-elements. Tag selectors are always scoped within a Block (e.g., &lt;code&gt;.feature-card &amp;gt; .title &amp;gt; span&lt;/code&gt;), so there's no external leakage. This decision is also mechanical: if you need independent responsibility, promote to Block; otherwise, use Element or tag selectors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Property Responsibility Separation
&lt;/h2&gt;

&lt;p&gt;The "parent handles layout, child handles internals" principle directly maps to CSS property placement rules.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Parent Block&lt;/span&gt;
&lt;span class="nc"&gt;.card-list&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;           &lt;span class="c1"&gt;// Container property → on itself&lt;/span&gt;
  &lt;span class="na"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;24px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;               &lt;span class="c1"&gt;// Container property → on itself&lt;/span&gt;

  &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;.feature-card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;margin-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;      &lt;span class="c1"&gt;// Item property → specified from parent&lt;/span&gt;
    &lt;span class="nl"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;               &lt;span class="c1"&gt;// Item property → specified from parent&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Child Block (separate file)&lt;/span&gt;
&lt;span class="nc"&gt;.feature-card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;           &lt;span class="c1"&gt;// Internal property → on itself&lt;/span&gt;
  &lt;span class="nl"&gt;text-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;      &lt;span class="c1"&gt;// Internal property → on itself&lt;/span&gt;

  &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;.title&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;18px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;       &lt;span class="c1"&gt;// Internal property → on itself&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Properties fall into three categories:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Container properties&lt;/strong&gt; (&lt;code&gt;display&lt;/code&gt;, &lt;code&gt;gap&lt;/code&gt;, &lt;code&gt;justify-content&lt;/code&gt;, etc.) → Write on the Block itself&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Item properties&lt;/strong&gt; (&lt;code&gt;margin&lt;/code&gt;, &lt;code&gt;flex&lt;/code&gt;, &lt;code&gt;order&lt;/code&gt;, &lt;code&gt;align-self&lt;/code&gt;, etc.) → Write in the parent Block's &lt;code&gt;&amp;gt; .child&lt;/code&gt; selector&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Internal properties&lt;/strong&gt; (&lt;code&gt;padding&lt;/code&gt;, &lt;code&gt;font-size&lt;/code&gt;, &lt;code&gt;color&lt;/code&gt;, etc.) → Write on the selector itself&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This three-way classification reflects how CSS layout models (Flexbox, Grid) work. Even before naming or file organization, this classification is the starting point of CSS architecture.&lt;/p&gt;

&lt;p&gt;Writing &lt;code&gt;margin-top&lt;/code&gt; on the child Block itself triggers a lint error. The error message says: "Specify &lt;code&gt;margin-top&lt;/code&gt; from the parent Block using the &lt;code&gt;&amp;gt; .child-name&lt;/code&gt; selector." No need to memorize — just write and the lint will tell you.&lt;/p&gt;

&lt;p&gt;This rule meets the three criteria for invariants from Part 2. &lt;strong&gt;Binary evaluation&lt;/strong&gt;: Whether &lt;code&gt;margin-top&lt;/code&gt; is in the parent's &lt;code&gt;&amp;gt; .child&lt;/code&gt; or on the child itself can be unambiguously determined. &lt;strong&gt;Syntax-verifiable&lt;/strong&gt;: Determined by analyzing SCSS selector structure alone; no runtime needed. &lt;strong&gt;Locally verifiable&lt;/strong&gt;: Only the target SCSS file needs to be examined. That's why automated lint verification works.&lt;/p&gt;

&lt;h2&gt;
  
  
  1 Block = 1 File
&lt;/h2&gt;

&lt;p&gt;SpiraCSS separates files by Block.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;feature-card/
├── feature-card.scss      ← Root Block
└── scss/
    ├── card-header.scss   ← Child Block
    ├── card-body.scss     ← Child Block
    └── index.scss         ← @use aggregation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The root Block file loads child Blocks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="k"&gt;@use&lt;/span&gt; &lt;span class="s2"&gt;"sass:meta"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nc"&gt;.feature-card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;@include&lt;/span&gt; &lt;span class="nd"&gt;meta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load-css&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"scss"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;.card-header&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// @rel/scss/card-header.scss&lt;/span&gt;
    &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;.card-body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// @rel/scss/card-body.scss&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This structure has three advantages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Physical prevention of class name collisions&lt;/strong&gt;: You can't create two files with the same name in the same folder. This is not just a rule; the structure makes collisions impossible&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Forced responsibility separation&lt;/strong&gt;: Child Blocks write only their own internals in their own files. There's no way to interfere with the parent's layout&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Navigation without searching&lt;/strong&gt;: &lt;code&gt;@rel&lt;/code&gt; comments link between files, enabling one-click navigation to related files&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Variant and State Separation
&lt;/h2&gt;

&lt;p&gt;BEM modifiers used a single mechanism to express both visual variations and dynamic states. SpiraCSS separates these into two:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;HTML&lt;/th&gt;
&lt;th&gt;SCSS&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Variant&lt;/td&gt;
&lt;td&gt;Static variations (size, color scheme)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;data-variant="primary"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;amp;[data-variant="primary"]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;State&lt;/td&gt;
&lt;td&gt;Dynamic states (open/close, selection, loading)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;data-state="active"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;amp;[data-state="active"]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="nc"&gt;.feature-card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;data-variant&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"highlight"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#f0f8ff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// --interaction&lt;/span&gt;
  &lt;span class="k"&gt;@at-root&lt;/span&gt; &lt;span class="k"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;box-shadow&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="mi"&gt;.3s&lt;/span&gt; &lt;span class="n"&gt;ease&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;data-state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"disabled"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="mi"&gt;.5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nl"&gt;pointer-events&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;box-shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt; &lt;span class="m"&gt;8px&lt;/span&gt; &lt;span class="nf"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="mi"&gt;.1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Variants go in the base structure section; States go in the &lt;code&gt;--interaction&lt;/code&gt; section. Stylelint also enforces this placement.&lt;/p&gt;

&lt;h2&gt;
  
  
  SCSS Section Structure
&lt;/h2&gt;

&lt;p&gt;The internal file structure is also defined. A single Block file consists of up to three sections:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Base structure&lt;/strong&gt;: Block layout, child placement, Variants&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;--shared&lt;/strong&gt; (optional): Styles shared among descendants within the Block&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;--interaction&lt;/strong&gt; (optional): States, hover, focus, ARIA, transitions, animations&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;"Where do I write hover?" "Where does transition go?" — these commonly ambiguous decisions in practice are resolved unambiguously by the section structure.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Big Picture So Far
&lt;/h2&gt;

&lt;p&gt;SpiraCSS's design can be summarized as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Single principle (parent handles layout, child handles internals)
  ├── Naming: Block (two-word) / Element (single-word)
  ├── Structure: Element &amp;gt; Block forbidden, Element depth limit
  ├── Properties: Container / Item / Internal — 3 categories
  ├── Files: 1 Block = 1 file
  ├── State: Variant / State separation
  └── Sections: Base / --shared / --interaction
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every rule derives from the single principle, and every rule is verifiable by Stylelint. No need to memorize conventions — just write and the lint will tell you.&lt;/p&gt;

&lt;p&gt;The next part covers the tools and procedures for adopting this design in practice. How do you integrate it into a real project, and how do humans and AI coexist?&lt;/p&gt;




&lt;p&gt;SpiraCSS's design specs, tools, and source code are all open source.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SpiraCSS: &lt;a href="https://spiracss.jp" rel="noopener noreferrer"&gt;https://spiracss.jp&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/zetsubo-dev/spiracss" rel="noopener noreferrer"&gt;https://github.com/zetsubo-dev/spiracss&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>css</category>
      <category>webdev</category>
      <category>ai</category>
      <category>frontend</category>
    </item>
    <item>
      <title>CSS Architecture Lint in the AI Era — TypeScript-Style Errors That Tell You How to Fix It</title>
      <dc:creator>zetsubo</dc:creator>
      <pubDate>Thu, 05 Feb 2026 09:58:33 +0000</pubDate>
      <link>https://dev.to/zetsubo/css-architecture-lint-in-the-ai-era-typescript-style-errors-that-tell-you-how-to-fix-it-32cg</link>
      <guid>https://dev.to/zetsubo/css-architecture-lint-in-the-ai-era-typescript-style-errors-that-tell-you-how-to-fix-it-32cg</guid>
      <description>&lt;p&gt;In Part 2, I introduced the idea of defining "invariants" to prevent design degradation (drift). A state in which all invariants are satisfied — that's architecture-compliant code, and it shows up as zero lint errors. But defining invariants alone isn't enough. Detecting violations, communicating how to fix them, and driving corrections — only when this loop runs does the design hold.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two Approaches: Upfront Teaching vs. Post-Hoc Feedback
&lt;/h2&gt;

&lt;p&gt;There are broadly two ways to maintain CSS architecture.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Upfront teaching&lt;/strong&gt;: Write guidelines, educate the team, verify through reviews. For AI agents, communicate rules through instruction files (&lt;code&gt;llms.txt&lt;/code&gt;, &lt;code&gt;AGENTS.md&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Post-hoc feedback&lt;/strong&gt;: Verify what's written, return errors with fix instructions. Humans see errors in the editor and fix them; AI parses lint output and self-corrects.&lt;/p&gt;

&lt;p&gt;Part 1 covered the limits of upfront teaching. The more rules there are, the higher the memorization cost; decisions become subjective; AI accuracy drops as context grows.&lt;/p&gt;

&lt;p&gt;Post-hoc feedback doesn't require the author to know the rules beforehand. Write, see the error, fix it. Starting from zero knowledge, the code converges on architecture compliance simply by running through the loop.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learning from TypeScript's Feedback Design
&lt;/h2&gt;

&lt;p&gt;TypeScript's type system is a successful example of post-hoc feedback.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// TypeScript error message&lt;/span&gt;
&lt;span class="c1"&gt;// Type 'string' is not assignable to type 'number'.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hello&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What makes this error effective is that it contains three pieces of information:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;What's wrong&lt;/strong&gt;: The types don't match&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Where&lt;/strong&gt;: Assignment to &lt;code&gt;count&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How to fix it&lt;/strong&gt;: Pass a &lt;code&gt;number&lt;/code&gt; value&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Developers don't need to memorize all of TypeScript's type rules. The compiler tells them as they write code, so just following the feedback converges the code to type safety.&lt;/p&gt;

&lt;p&gt;Architecture lint needs the same structure.&lt;/p&gt;

&lt;p&gt;But here's the key distinction: traditional CSS lint checks &lt;em&gt;what you wrote&lt;/em&gt; — naming patterns, property duplication. Architecture lint checks &lt;em&gt;where you wrote it&lt;/em&gt; — whether the structure itself follows design rules. This is a fundamentally different level of verification.&lt;/p&gt;

&lt;h2&gt;
  
  
  Designing Error Messages for Architecture Lint
&lt;/h2&gt;

&lt;p&gt;Traditional CSS lint tends to focus on surface-level checks.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Traditional lint error (typical)
Expected indentation of 2 spaces (indentation)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fix here is obvious — adjust the spaces. But this is surface-level formatting. It doesn't verify component structure or property placement — the architectural concerns that cause real design drift.&lt;/p&gt;

&lt;p&gt;An error message that functions as feedback should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Feedback-style lint error
"margin-top" is an item property — it must be specified
from the parent Block using the `&amp;gt; .child-name` selector,
not inside the child Block itself.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This message contains:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;What's wrong&lt;/strong&gt;: &lt;code&gt;margin-top&lt;/code&gt; is in the wrong place&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why it's wrong&lt;/strong&gt;: Item properties must be specified from the parent&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How to fix it&lt;/strong&gt;: Write it in the parent Block's &lt;code&gt;&amp;gt; .child-name&lt;/code&gt; selector&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here's what the Stylelint output would look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/components/feature-card/feature-card.scss
  4:3  ✖  "margin-top" is an item property — it must be    spiracss/property-placement
          specified from the parent Block using
          "&amp;gt; .child-name" selector, not inside the
          child Block itself.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;File path, line number, error message, rule name — all the information an AI agent needs to parse and act on. The correction flow looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Extract from lint output: "feature-card.scss line 4, move margin-top to parent"
2. Remove the margin-top declaration from feature-card.scss
3. In each parent Block that uses feature-card, add under its selector (SCSS nesting):
   .parent-block {
     &amp;gt; .feature-card { margin-top: ... }
   }
4. Re-run lint → confirm zero errors
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that violation &lt;em&gt;detection&lt;/em&gt; is always local — lint spots &lt;code&gt;margin-top&lt;/code&gt; in the wrong file. The &lt;em&gt;fix&lt;/em&gt; may touch another file (the parent), but the error message tells you exactly where, with no project-wide scanning required.&lt;/p&gt;

&lt;p&gt;Even humans with no CSS architecture experience can understand the fix from this message.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Structure of a Feedback Loop
&lt;/h2&gt;

&lt;p&gt;Whether the author is human or AI, the loop structure is the same:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Write → Run lint → Detect errors → Fix → Run lint → ... (until zero errors)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For humans:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Write SCSS&lt;/li&gt;
&lt;li&gt;Stylelint shows errors in real-time in the editor&lt;/li&gt;
&lt;li&gt;Read error messages and fix&lt;/li&gt;
&lt;li&gt;Save → re-validate → fix complete&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For AI agents:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Generate SCSS&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;stylelint&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Parse output and fix according to error messages&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;stylelint&lt;/code&gt; again → repeat until zero errors&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The key point is that this loop has the same exit condition. Zero errors — that's architecture-compliant code, and it converges to the same standard regardless of who wrote it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Error Message Design Principles
&lt;/h2&gt;

&lt;p&gt;For the feedback loop to function, error messages must meet these conditions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Be specific&lt;/strong&gt;: Not "invalid naming" but "Blocks must use a two-word kebab-case name (e.g., &lt;code&gt;hero-container&lt;/code&gt;)"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Include fix instructions&lt;/strong&gt;: Communicate not just what's wrong, but how to fix it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Be self-contained&lt;/strong&gt;: Understandable without referencing external documentation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Be machine-readable&lt;/strong&gt;: Structured so AI can parse and convert to actionable fixes&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Error messages are both "documentation" and a "teacher." They must be comprehensible to a human encountering the rule for the first time and convertible to executable fixes when read by AI — that's the quality bar.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feedback Effectiveness in Practice
&lt;/h2&gt;

&lt;p&gt;Whether this design works hinges on one question: "Can you really fix issues just from lint errors?"&lt;/p&gt;

&lt;p&gt;I've implemented and used this approach in production with &lt;a href="https://spiracss.jp" rel="noopener noreferrer"&gt;SpiraCSS&lt;/a&gt;. Out of the box, traditional CSS lint mainly covers naming conventions and property duplication. SpiraCSS's Stylelint plugin goes further — architecture-aware lint that extends verification to component structure and property placement. As of 2025, when SpiraCSS was first released, I'm not aware of any other CSS architecture methodology that performs this level of structural verification through lint — integrated into an AI agent's autonomous correction loop.&lt;/p&gt;

&lt;p&gt;This level of test coverage — over 650 patterns — would have been impractical to develop by hand. AI-assisted development made it possible.&lt;/p&gt;

&lt;p&gt;From practical experience, I can say this: if invariants are properly designed, fixes are usually possible from error messages alone. Because invariants are "binary" and "locally verifiable," the location and method of correction are determined with little ambiguity.&lt;/p&gt;

&lt;p&gt;With ambiguous conventions, you get hesitation: "Should I do this? Or that?" But an invariant violation almost always has one clear answer: "Fix it like this." That's why the feedback loop converges.&lt;/p&gt;

&lt;p&gt;The next part looks at the concrete design principles and structure of SpiraCSS, which implements this approach. How many rules can be derived from a single principle?&lt;/p&gt;




&lt;p&gt;SpiraCSS's design specs, tools, and source code are all open source.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SpiraCSS: &lt;a href="https://spiracss.jp" rel="noopener noreferrer"&gt;https://spiracss.jp&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/zetsubo-dev/spiracss" rel="noopener noreferrer"&gt;https://github.com/zetsubo-dev/spiracss&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>css</category>
      <category>webdev</category>
      <category>ai</category>
      <category>frontend</category>
    </item>
    <item>
      <title>CSS Drift in the AI Era — Why Conventions Break Down and How Machine-Verifiable Rules Fix It</title>
      <dc:creator>zetsubo</dc:creator>
      <pubDate>Tue, 03 Feb 2026 10:48:19 +0000</pubDate>
      <link>https://dev.to/zetsubo/what-does-resilient-mean-defining-drift-and-invariants-16ne</link>
      <guid>https://dev.to/zetsubo/what-does-resilient-mean-defining-drift-and-invariants-16ne</guid>
      <description>&lt;p&gt;In &lt;a href="https://dev.to/zetsubo/how-should-css-architecture-evolve-in-the-age-of-ai-coding-agents-317k"&gt;Part 1&lt;/a&gt;, I argued that CSS architecture should shift from "rules to memorize and follow" to a "feedback system." But what exactly does "resilient" (drift-resistant) design mean? This part defines the phenomenon of design degradation as "drift" and introduces the concept of "invariants" to prevent it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Drift — How Design Breaks Down
&lt;/h2&gt;

&lt;p&gt;CSS design doesn't collapse all at once. Small decision inconsistencies accumulate, and by the time you notice, coherence is lost. I call this "drift."&lt;/p&gt;

&lt;p&gt;In this series, "breaking down" refers specifically to three things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Component boundary ambiguity&lt;/strong&gt;: Where one component ends and another begins differs from person to person&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Layout responsibility confusion&lt;/strong&gt;: Whether margin belongs to parent or child varies file by file&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Naming and structural inconsistency&lt;/strong&gt;: Naming conventions and file organization vary across the project&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, here are common forms of drift in BEM-based projects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One team member writes &lt;code&gt;margin&lt;/code&gt; on the child component itself; another specifies it from the parent&lt;/li&gt;
&lt;li&gt;"Is this element an independent component or part of its parent?" — answers differ by person&lt;/li&gt;
&lt;li&gt;Modifier granularity varies from file to file&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these are "rule violations." The interpretive latitude in guidelines allows decision drift. And that latitude widens as teams grow.&lt;/p&gt;

&lt;p&gt;Introducing AI coding agents doesn't change the structure. Even with guidelines packed into the context window, output varies where judgment is required. Human drift simply becomes AI drift.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Conventions Can't Prevent Drift
&lt;/h2&gt;

&lt;p&gt;Convention-based designs share a structural weakness:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Memorization burden&lt;/strong&gt;: The longer the guidelines, the harder it is for everyone to accurately remember and apply them&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subjective decisions&lt;/strong&gt;: Questions like "where does one component end?" have no objective answer&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verification difficulty&lt;/strong&gt;: Whether something violates a convention can't be determined mechanically — you can only rely on reviews&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When all three of these are present, drift is inevitable. Reviews maintain quality only while the reviewer's judgment stays consistent. And reviewers are human — their judgment drifts too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Invariants — Eliminating Interpretive Ambiguity
&lt;/h2&gt;

&lt;p&gt;Programming has the concept of "invariants" — conditions that must hold at every point in a program. An array index staying within bounds, an account balance never going negative — these conditions are mechanically enforced through types and assertions.&lt;/p&gt;

&lt;p&gt;The same idea applies to CSS architecture. Eliminate rules whose answers change based on interpretation, and adopt only conditions that can be mechanically evaluated as true or false.&lt;/p&gt;

&lt;p&gt;Concrete examples:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Convention-based (requires interpretation)&lt;/th&gt;
&lt;th&gt;Invariant (mechanically verifiable)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;"Decompose components into appropriate granularity"&lt;/td&gt;
&lt;td&gt;"A Block (component) can only contain Blocks or Elements as direct children" (details in Part 4)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"Parents manage layout"&lt;/td&gt;
&lt;td&gt;"&lt;code&gt;margin-top&lt;/code&gt; can only be specified from the parent's &lt;code&gt;&amp;gt; .child&lt;/code&gt; selector"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"Keep naming consistent"&lt;/td&gt;
&lt;td&gt;"Blocks use two-word kebab-case; Elements use a single word"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"Distinguish between state and variation"&lt;/td&gt;
&lt;td&gt;"Variants use &lt;code&gt;data-variant&lt;/code&gt;; States use &lt;code&gt;data-state&lt;/code&gt;"&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The left column requires human interpretation to judge correctness. The right column can be judged through pattern matching on class names and selector structure. Automated lint verification becomes possible.&lt;/p&gt;

&lt;p&gt;The examples in the right column are actual invariants used in &lt;a href="https://spiracss.jp" rel="noopener noreferrer"&gt;SpiraCSS&lt;/a&gt;, which I've developed and use in production. Part 4 covers them in detail.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design Criteria for Invariants
&lt;/h2&gt;

&lt;p&gt;Not every rule can become an invariant. To function as one, a rule needs these properties:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Binary evaluation&lt;/strong&gt;: Violation or compliance is determined unambiguously (no gray zones)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Syntax-verifiable&lt;/strong&gt;: Can be determined through source code syntax analysis alone (no runtime needed)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Locally verifiable&lt;/strong&gt;: Can be determined by looking at the target file alone (no project-wide scanning needed)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If the design is composed entirely of rules meeting these criteria, a lint tool can serve as the "gatekeeper." Humans don't need to make judgment calls — just follow the tool's verdict. The same goes for AI agents.&lt;/p&gt;

&lt;p&gt;"Isn't this just freezing preferences rather than defining 'correctness'?" That's a fair objection. "Write margin from the parent" isn't the only correct approach. But there's value in fixing design preferences to make all authors converge on the same output — especially in an era where the pool of authors includes both humans and AI. Having an unwavering standard itself supports team productivity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Invariants Alone Aren't Enough
&lt;/h2&gt;

&lt;p&gt;However, defining invariants alone won't preserve the design. Without detecting violations and communicating "what's wrong and how to fix it," corrections don't happen. Defining invariants, detecting violations, and presenting fix instructions — this concrete sequence is the "feedback loop" that turns the feedback system from Part 1 into something actionable.&lt;/p&gt;

&lt;p&gt;The next part dives deeper into the design of this feedback loop. When lint returns not just "what's wrong" but "how to fix it," how should those error messages be designed?&lt;/p&gt;




&lt;p&gt;SpiraCSS's design specs, tools, and source code are all open source.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SpiraCSS: &lt;a href="https://spiracss.jp" rel="noopener noreferrer"&gt;https://spiracss.jp&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/zetsubo-dev/spiracss" rel="noopener noreferrer"&gt;https://github.com/zetsubo-dev/spiracss&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>css</category>
      <category>webdev</category>
      <category>ai</category>
      <category>frontend</category>
    </item>
    <item>
      <title>How Should CSS Architecture Evolve in the Age of AI Coding Agents?</title>
      <dc:creator>zetsubo</dc:creator>
      <pubDate>Mon, 02 Feb 2026 00:05:56 +0000</pubDate>
      <link>https://dev.to/zetsubo/how-should-css-architecture-evolve-in-the-age-of-ai-coding-agents-317k</link>
      <guid>https://dev.to/zetsubo/how-should-css-architecture-evolve-in-the-age-of-ai-coding-agents-317k</guid>
      <description>&lt;p&gt;CSS architecture has long been built around human-centered workflows. The naming conventions of BEM/SMACSS and the design philosophy of utility-first both assume authors who memorize rules, make judgment calls, and maintain consistency through review.&lt;/p&gt;

&lt;p&gt;But that assumption is changing. AI coding agents like Claude Code, Cursor, and Windsurf are becoming a normal part of UI implementation. In the Tailwind ecosystem, &lt;code&gt;llms.txt&lt;/code&gt; — a file that feeds project rules and usage patterns to AI — has emerged as one approach to guide AI output.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;llms.txt&lt;/code&gt; represents a "teach the rules to AI" approach. But the real question is: can we design an architecture that doesn't break down even without that teaching?&lt;/p&gt;

&lt;p&gt;For tech leads and technical directors responsible for maintaining CSS quality across a team — and for developers ready to move beyond "CSS that just works" toward professional-grade architecture — this is an unavoidable question.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Limits of Convention-Based Design
&lt;/h2&gt;

&lt;p&gt;Utility-first is often said to pair well with AI. Since class names directly represent styles, AI can reproduce designs more easily. But whether the output is "production quality" is a separate question. For example, should a card component's margin belong to the parent or the child? Should visually identical elements be shared or duplicated? These structural decisions remain even with utility-first.&lt;/p&gt;

&lt;p&gt;Convention-based designs like BEM face the same challenge. The model of maintaining quality through conventions + reviews shares the same weaknesses whether the author is human or AI:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There's a cost to memorizing conventions (or teaching them to AI)&lt;/li&gt;
&lt;li&gt;Decisions are subjective, and review standards tend to drift&lt;/li&gt;
&lt;li&gt;Consistency erodes as teams grow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even if you feed AI a long instruction document to enforce rules, accuracy drops as context grows. The same problem humans face reappears in a different form.&lt;/p&gt;

&lt;h2&gt;
  
  
  From "Rules to Follow" to "Feedback Systems"
&lt;/h2&gt;

&lt;p&gt;CSS architecture should shift from "rules to memorize and follow" to "feedback systems."&lt;/p&gt;

&lt;p&gt;TypeScript's type system is a useful reference. You don't need to memorize type rules — just write code and the compiler tells you "this is wrong" and "fix it like this." Developers simply follow the feedback, and the code converges to type safety.&lt;/p&gt;

&lt;p&gt;CSS architecture needs the same structure. Instead of teaching rules upfront, the system should respond to what's written with "what's wrong" and "how to fix it." Humans read error messages and fix issues; AI agents parse lint output and self-correct — a feedback loop where the design converges regardless of who writes the code.&lt;/p&gt;

&lt;h2&gt;
  
  
  About This Series
&lt;/h2&gt;

&lt;p&gt;This series uses &lt;a href="https://spiracss.jp" rel="noopener noreferrer"&gt;SpiraCSS&lt;/a&gt; — a CSS architecture methodology I created and use in production — as a concrete example to explore what CSS architecture should look like in the AI era.&lt;/p&gt;

&lt;p&gt;Existing CSS methodologies assume humans memorize and follow naming conventions and file structures. SpiraCSS takes a different approach. It makes component structure and property placement mechanically verifiable through Stylelint, with error messages that tell you exactly what to fix and how — going well beyond conventional CSS lint, which typically covers only naming and property duplication.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Part 2&lt;/strong&gt;: CSS Drift in the AI Era — Why Conventions Break Down and How Machine-Verifiable Rules Fix It&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 3&lt;/strong&gt;: CSS Architecture Lint in the AI Era — TypeScript-Style Errors That Tell You How to Fix It&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 4&lt;/strong&gt;: Parent Owns Layout — A New CSS Architecture for the AI Era — Drift-Resistant by Design&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 5&lt;/strong&gt;: Same Lint, Same Result — A Stylelint Toolchain for Humans and AI Agents&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your AI agents keep producing inconsistent CSS, or your team keeps raising the same issues in review — this series may offer a useful perspective.&lt;/p&gt;

&lt;p&gt;Next up, we start by defining what resilience (drift resistance) concretely means.&lt;/p&gt;

&lt;p&gt;How does your team maintain CSS design consistency? Guidelines, reviews, lint — what's working and what's breaking down?&lt;/p&gt;




&lt;p&gt;SpiraCSS's design specs, tools, and source code are all open source.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SpiraCSS: &lt;a href="https://spiracss.jp" rel="noopener noreferrer"&gt;https://spiracss.jp&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/zetsubo-dev/spiracss" rel="noopener noreferrer"&gt;https://github.com/zetsubo-dev/spiracss&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>css</category>
      <category>webdev</category>
      <category>frontend</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
