plugins/style-agent/skills/inline-style-to-class/SKILL.md
Use when the developer wants to convert an inline style attribute, JSX style object, or <style> block into a named CSS class appended to the project stylesheet. Replaces hard-coded colors, units, and values with CSS variables — reusing an existing variable when one matches and creating a new one when none does.
npx skillsauth add shawn-sandy/acss-plugins inline-style-to-classInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Convert an inline style attribute, JSX style object, or <style> block into a single, semantically named CSS class. Appends the result to a stylesheet detected from the project's own file conventions — works with plain CSS, SCSS, or Sass-indented syntax. The inverse operation of /css-to-class.
Every concrete value in the migrated declarations is replaced with a CSS variable: if a custom property in the project already holds that value, the class references it; otherwise a new variable is created and declared. The original literal is always kept as the var() fallback, so the class renders even when the variable is absent.
| Form | Example |
|---|---|
| IDE selection | Select an element or style block in your editor, then run /inline-style-to-class |
| HTML inline attribute | <div style="background: var(--surface-1); padding: 1rem"> |
| JSX style object | <Button style={{ backgroundColor: theme.primary, padding: 8 }}> |
| <style> block | <style>.hero { color: red; padding: 2rem; }</style> |
When invoked from an IDE (VS Code, JetBrains), Claude Code includes the selected text along with the source file path and line range. The skill detects this by checking for file-context metadata attached to the user's prompt (a file path with a line range and the selected content).
When IDE selection context is present:
<div style="..." class="existing">), the element tag and existing classes are preserved during refactoring. In-place editing is enabled — the skill can safely remove the style attribute and add the class reference.style attribute value (e.g. background: red; padding: 1rem) without the enclosing element tag, the skill uses Read to load the surrounding lines from the source file and expand context to the full containing element. If expansion succeeds, the full element is used for in-place editing. If the element boundary cannot be determined (e.g. the tag spans many lines or is ambiguous), in-place editing is disabled for this invocation — the refactored source is emitted to chat instead, and the summary notes which file and line to update manually.When no IDE selection context is detected, the skill falls back to parsing the input from the user's prompt text (the existing behavior).
[a-z][a-z0-9-]*).name is supplied, apply this sanitisation pipeline in order:
-.[a-z0-9-].[a-z].AskUserQuestion for a valid name instead of emitting an invalid identifier. Warn the user whenever any coercion occurred.name is omitted: auto-generate via the algorithm below. When the result is ambiguous or ≤ 3 chars, ask via AskUserQuestion with the generated name pre-filled as the suggestion.button → btn, section → section, header → header, nav → nav, ul/li → list, input → input, img → img, a → link. All other tags use the tag name verbatim (e.g. div → div).background / background-color / color → bgpadding / padding-* → padmargin / margin-* → gapfont-size / font-weight / font-* → typedisplay / flex / grid → layoutborder / border-* → borderwidth / height / min-* / max-* → sizeposition / top / left / z-index → pos-: <tag-abbrev>-<role>, e.g. div-bg, btn-pad. If no tag is available, use the role hint alone.-. Strip leading/trailing -.custom-class and warn. If the result is 1–3 chars, ask via AskUserQuestion with the suggestion pre-filled.**/node_modules/**, **/.git/**, **/dist/**, **/build/**:
src/**/*.{css,scss,sass}styles/**/*.{css,scss,sass}app/**/*.{css,scss,sass}*.{css,scss,sass} (repo root).css, .scss, .sass).\n.globals, main, index, styles, app, or base), use that one. Otherwise prompt with AskUserQuestion to pick.Run this alongside Stylesheet discovery so the class can be tokenized.
^\s*(--[A-Za-z0-9_-]+)\s*:\s*([^;\n]+);? — the identifier class allows uppercase and underscores (custom-property names are case-sensitive), and the trailing ; is optional so semicolon-less .sass declarations and a block's last unterminated declaration are still captured. Build a reverse map of normalised value → variable name (see normalisation under Value tokenizing rules). This map drives reuse — when a migrated value matches, reference the existing variable instead of creating one.--color-* vs --clr-* vs --c-*; spacing as --space-* vs --spacing-* vs --size-*. Use the detected prefix when creating new variables of that category. If a category has no precedent, fall back to the default semantic prefix listed below.tokens.{css,scss,sass}, variables.{css,scss,sass}, or _variables.{scss,sass}.:root { } block (in any discovered stylesheet, preferring an entry file named globals, main, index, styles, app, or base).:root { } block will be created at the top of it.
Remember this target for the "Declare new variables" workflow step.Every concrete value is replaced with a variable. Reuse always wins over creation.
#hex (3 or 6 digit, matched by #([0-9a-fA-F]{6}|[0-9a-fA-F]{3})), rgb()/rgba(), hsl()/hsla(), or a named color (red, white, …).px, rem, em, %, vh, vw, ch, …).border-radius, font-size, box-shadow, z-index).flex, block, none, auto, center.url(...) paths, quoted strings, and any case-sensitive identifier, so a value never matches a variable that points at a different resource. Matching is exact on the normalised form only — values are never converted across color formats (no hex↔rgb↔hsl) and units are never converted (16px ≠ 1rem). Note this limitation in the summary when relevant.var(...) reference — i.e. the trimmed value begins with var( — pass through unchanged. Use this prefix check rather than a full regex, so references whose fallback contains nested parens (e.g. var(--x, calc(...)) or var(--x, color-mix(...))) are still recognised./* unresolved */ placeholder behavior.When no existing variable matches, create one:
--color-1, --color-2, … (or a descriptive --color-<role> when the property implies one)--space-*width/height/min-*/max-*) → --size-*--radius-*--font-size-* (or --text-* if that prefix is already used)--shadow-*--z-*--<property>-<value> (e.g. display: flex → --display-flex). and spaces with -, strip any character not in [a-z0-9-], collapse consecutive hyphens (e.g. 1.5rem → space-1-5rem).-2, -3, … until unique.Parse input. Check for IDE selection context first, then fall back to prompt text:
<style> block) from the selected content using the same detection rules below.style="..." or style='...'; split on ;; trim; parse property: value pairs. Extract the surrounding element tag if present.style={{...}}; extract the object literal body; split key/value pairs. Convert each camelCase key to kebab-case (insert - before each uppercase letter, then lowercase the whole key). For numeric literal values, emit a coercion warning: "numeric value — verify unit". For any value that is a JS expression (not a string or number literal), emit a /* unresolved: <expr> */ placeholder.<style> block — extract content between <style> and </style>; parse rules. If one rule, use its declarations. If multiple rules, ask via AskUserQuestion: merge all declarations or pick a specific rule.Determine the class name. Apply the Name rules above. Use the [name] argument if provided. Otherwise apply the auto-name algorithm. If the generated name is ambiguous or ≤ 3 chars, ask via AskUserQuestion with the suggestion pre-filled.
Discover the target stylesheet and existing variables. Follow the Stylesheet discovery and Variable discovery sections. Confirm the chosen file with the user only when the choice is ambiguous.
Build the CSS class block. Emit using the detected syntax flavor and indentation:
/* from: <source summary — e.g. "style attr on <div>", "JSX style object", or "<style> block" */.property: var(--name, <original-literal>); with the original literal kept as the fallback. Skip values that are already var(...) references and unresolved expressions./* <property>: unresolved — was JS expression */./* verify unit */ inline comment after the declaration..sass) syntax, omit braces and use the detected indentation.Declare new variables. For each variable created in step 4, write its --name: value; declaration to the target located in Variable discovery step 3 (a tokens/variables file, an existing :root block, or — if none exists — a new :root { } block inserted at the top of the target stylesheet). Match the file's detected syntax flavor, indentation, and trailing-newline convention; for .sass, omit braces. Reused (already-existing) variables are not re-declared.
Append to the target stylesheet. Use Edit to append the class block, preceded by one blank line. Preserve any trailing newline the file already had.
Emit refactored source. Produce a clean version of the original input with the inline style removed:
style="..." attribute entirely if all declarations migrated; if some were unresolved, keep only unmigrated declarations in the attribute. Add the new class to any existing class attribute (append to the list); if none exists, add class="<name>". Preserve all other attributes unchanged (data-*, id, aria-*).className. For partially-migrated objects, preserve remaining key/value pairs in the style prop.<style> block — emit a comment noting the rule was extracted: /* rule moved to .<name> in <stylesheet path> */; do not rewrite the <style> block automatically.Edit to replace the original selected text in the source file with the refactored version. This removes the inline style and adds the class reference directly in the user's file — no manual copy-paste needed. If the edit would be ambiguous (the selected text appears more than once in the file), fall back to emitting the refactored source in chat and note which file and line to update.Print a summary:
value → --name that matched an existing variable.--name: value and the file it was declared in.development
Internal orchestrator for /kit-create, /kit-list, /kit-sync, /kit-update and Form/HTML/Style-Tune modes. Per-component generation lives in component-<name> skills; do not auto-trigger for component requests.
data-ai
Use when the user asks to generate, create, or scaffold a Table — accessible data table with caption, scope headers, responsive scroll wrapper, and sortable column support.
tools
Use when the user asks to generate, create, or scaffold a Popover — accessible tooltip/popover using the Popover API with focus trap, aria-expanded, and light-dismiss.
tools
Use when the user asks to generate, create, or scaffold a Nav — accessible navigation landmark with aria-label, current-page link marking, and horizontal/vertical layout.