Skip to content

inline-style-to-class skill

The inline-style-to-class skill drives /inline-style-to-class. It owns all the behavior described in that command — input parsing, name generation, stylesheet discovery, class emission, file append, source refactoring, and summary output. The command is a thin shell that delegates everything here.

Triggered by/inline-style-to-class
Allowed toolsRead, Glob, Grep, Bash, Write, Edit, AskUserQuestion
OutputA named CSS class appended to the project stylesheet + refactored source with the inline style removed
FormExample
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>
  • Max 20 characters, kebab-case only ([a-z][a-z0-9-]*).
  • If name is supplied, it is sanitised in order: lowercase → spaces/underscores replaced with - → non-[a-z0-9-] characters stripped → leading hyphens and digits stripped → trailing hyphens stripped → consecutive hyphens collapsed → truncated to 20 characters. If the result is empty, AskUserQuestion is used.
  • If name is omitted, the auto-name algorithm runs (see below). Names of 3 characters or fewer trigger AskUserQuestion with the suggestion pre-filled.
  1. If an element tag is present, derive an abbreviation: buttonbtn, sectionsection, headerheader, navnav, ul/lilist, inputinput, imgimg, alink. All other tags use the tag name verbatim (divdiv).
  2. From the first declaration in the style source, extract a role hint from the property:
    • background / background-color / colorbg
    • padding / padding-*pad
    • margin / margin-*gap
    • font-size / font-weight / font-*type
    • display / flex / gridlayout
    • border / border-*border
    • width / height / min-* / max-*size
    • position / top / left / z-indexpos
    • Any other property — skip the role hint.
  3. Join with -: <tag-abbrev>-<role> (e.g. div-bg, btn-pad). If no tag is available, use the role hint alone.
  4. Truncate to 20 characters. Collapse double -. Strip leading/trailing -.
  5. If the result is empty, use custom-class and warn. If the result is 1–3 characters, use AskUserQuestion with the suggestion pre-filled.

The skill searches for stylesheets in priority order, excluding **/node_modules/**, **/.git/**, **/dist/**, **/build/**:

  1. src/**/*.{css,scss,sass}
  2. styles/**/*.{css,scss,sass}
  3. app/**/*.{css,scss,sass}
  4. *.{css,scss,sass} (repo root)

From the matches, the skill detects syntax flavor (.css, .scss, .sass), indentation (from the first non-empty rule body), and whether the file has a trailing newline.

Selection: exactly one candidate → use it. Multiple candidates with one clear entry file named globals, main, index, styles, app, or base → use that one. Otherwise prompt with AskUserQuestion. No stylesheet found → emit the CSS block to chat only.

  1. Parse input. Detect the input form:

    • HTML inline attribute — match style="..." or style='...'; split on ;; parse property: value pairs. Extract the surrounding element tag if present.
    • JSX style object — match style={{...}}; extract the object literal body; convert camelCase keys to kebab-case. Numeric literal values get a “verify unit” warning. JS expression values become /* unresolved: <expr> */ placeholders.
    • <style> block — extract content between <style> and </style>; parse rules. If multiple rules are present, ask via AskUserQuestion whether to merge all declarations or pick a specific rule.
  2. Determine the class name. Apply the name rules. Use the [name] argument if provided. Otherwise apply the auto-name algorithm. If the generated name is ambiguous or 3 characters or fewer, ask via AskUserQuestion with the suggestion pre-filled.

  3. Discover the target stylesheet. Follow the stylesheet discovery process. Confirm the chosen file with the user only when the choice is ambiguous.

  4. Build the CSS class block. Emit using the detected syntax flavor and indentation:

    • Comment header: /* from: <source summary> */ (e.g. "style attr on <div>", "JSX style object", "<style> block").
    • One property per line.
    • Unresolved JSX expressions: /* <property>: unresolved — was JS expression */.
    • Numeric values without units: preserve the value and append /* verify unit */ inline.
    • For Sass-indented (.sass) syntax, omit braces and use the detected indentation.
  5. 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.

  6. Emit refactored source. Produce a clean version of the original input with the inline style removed:

    • HTML — remove the style="..." attribute entirely if all declarations migrated; if some were unresolved, keep only unmigrated declarations in the attribute. Add the new class to the existing class attribute (or create one). Preserve all other attributes unchanged.
    • JSX — same logic using className. Partially-migrated objects preserve remaining key/value pairs in the style prop.
    • <style> block — emit /* rule moved to .<name> in <stylesheet path> */; do not rewrite the <style> block automatically.
  7. Print a summary. Reports: class name (provided or auto-generated), any coercion warnings, target stylesheet path and confirmation of append (or a note if no file was found), declaration count migrated, unresolved JS expression count, and numeric-value unit warnings.

JSX style object keys are converted from camelCase to kebab-case before emitting:

// Input
style={{ backgroundColor: theme.primary, fontSize: 16 }}
// Output CSS
background-color: /* unresolved — was JS expression */
font-size: 16; /* verify unit */

Given <div style="background: var(--surface-1); padding: 1rem">:

Appended to stylesheet:

/* from: style attr on <div> */
.div-bg {
background: var(--surface-1);
padding: 1rem;
}

Refactored HTML:

<div class="div-bg"></div>