| name | css-cleanup |
| description | Audit and refactor CSS in Obsidian .obs block files to follow Rock's styling priorities. Replaces inline styles, hard-coded values, and unnecessary scoped CSS with Rock utility classes and CSS variables. Use when the user says "clean up css", "css cleanup", "polish styles", "refactor css", "style audit", "fix the styling", "use rock utilities", or after running /convert-block on a block. Also use when reviewing .obs files and noticing inline styles, hard-coded hex colors, or excessive scoped CSS that could use Rock's utility system. Do NOT use for: writing new blocks, C# changes, JavaScript logic changes, or creating new SCSS files in styles-v2/. Do NOT use for general code review (use /review-conversion instead). |
| argument-hint | Path to .obs file or Category/BlockName (e.g. 'Core/TagsByLetter' or 'Rock.JavaScript.Obsidian.Blocks/src/Core/tagsByLetter.obs') |
| compatibility | Requires Claude Code CLI with access to the Rock RMS codebase. |
| metadata | {"version":"1.1","author":"Maxwell Eley"} |
Obsidian CSS Cleanup
You are refactoring CSS in Obsidian .obs block files to follow Rock's styling priority system.
The block must look identical before and after. This is a pure refactor — swap implementation details (inline styles, hard-coded values, redundant scoped rules) for Rock's utility classes and CSS variables, but the rendered result in the browser must not change. If a replacement would alter spacing, color, size, or layout even slightly, keep the original and leave a comment explaining why. The one exception: replacing Bootstrap numeric spacing (e.g., mb-3) with Rock semantic spacing (e.g., mb-spacing-sm) is expected — those values are close but intentionally theme-adaptive.
Target: $ARGUMENTS
CSS Priority Order
This is the hierarchy. Always prefer a higher-priority option before falling to the next:
- Rock Utility Classes — from
styles-v2/utilities/ and styles-v2/rock-utilities/. These are theme-aware, responsive, and consistent. Always the first choice.
- Block Classes — semantic CSS class names on block wrapper elements for structural layout. Check if the block's category already has shared styles in
styles-v2/blocks/_blocks-[category].scss.
- Scoped Styles —
<style scoped> rules. Use only when utilities cannot express the styling, OR when the styling is specific enough to justify a scoped rule (e.g., :deep() overrides, complex selectors, animations). Always use CSS variables for values inside scoped rules.
- Bootstrap — only as a last resort when no Rock utility exists for the same purpose.
Reference Routing
| Reference | Load When |
|---|
references/utility-catalog.md | Phase 2 — always (needed for replacement lookup) |
Phase 1: Locate and Read
- Resolve file paths. If given a block name like
Core/TagsByLetter, find:
- Main:
Rock.JavaScript.Obsidian.Blocks/src/[Category]/[camelCaseBlockName].obs
- Partials:
Rock.JavaScript.Obsidian.Blocks/src/[Category]/[camelCaseBlockName]/*.partial.obs
- Read every .obs file for this block (main + all partials).
- Check for block-level SCSS at
RockWeb/Styles/styles-v2/blocks/_blocks-[category].scss. If it exists, read it — these classes are already globally available and should not be duplicated in scoped styles.
Phase 2: Audit
Load references/utility-catalog.md now.
Scan each file and produce a findings table:
| # | File | Line | Category | Current | Replacement |
|---|
Anti-Pattern Categories
Scan for these, in order of severity:
- Inline styles —
style="..." on template elements. Replace with utility classes or scoped rules.
- Hard-coded spacing —
padding: 8px, margin: 16px, gap: 12px in scoped CSS. Replace with Rock spacing utilities (.gap-spacing-xs) or var(--spacing-*).
- Hard-coded colors — hex codes (
#fff, #ee7725), rgb(...), named colors in scoped CSS. Replace with var(--color-*) or Rock color utility classes.
- Hard-coded font sizes —
font-size: 14px. Replace with var(--font-size-*) or .font-size-* class.
- Hard-coded font weights —
font-weight: 600. Replace with var(--font-weight-*).
- Hard-coded border-radius —
border-radius: 4px. Replace with var(--rounded-*).
- Scoped CSS duplicating a utility — a scoped rule that just does
display: flex when .d-flex exists, or gap: 8px when .gap-spacing-xs exists.
- Bootstrap when Rock exists — e.g.,
mb-3 when mb-spacing-sm is the semantic Rock alternative.
- Missing
:deep() — scoped style targeting a child component's internal DOM without :deep().
- Excessive scoped rules — more than ~10 scoped rules in a single file; look for rules that could be replaced by utility classes on the element.
After the table, output a summary count by category.
Phase 3: Apply Decision Framework
For each finding, walk this tree:
1. Can a Rock utility class replace this?
YES → Use the utility class on the element. DONE.
2. Can a combination of Rock utility classes replace this?
YES → Use the classes. DONE.
3. Does the block's category SCSS already define a class for this?
YES → Use the existing block class. DONE.
4. Is a scoped style appropriate?
- The styling is block-specific layout that utilities can't express?
- It targets a child component's internals (needs :deep())?
- It's a complex selector, animation, or pseudo-element?
YES → Keep as scoped style, but tokenize all values:
spacing → var(--spacing-*)
colors → var(--color-*)
fonts → var(--font-size-*), var(--font-weight-*)
radii → var(--rounded-*)
5. No Rock utility exists and it's a standard CSS property?
→ Use Bootstrap if available. Otherwise, scoped style with CSS variables.
6. No replacement produces an exact visual match?
→ Leave the original code as-is. Do not force a swap that changes appearance.
When in doubt, leave it alone. A hard-coded value that renders correctly is better than a token swap that shifts layout by a few pixels.
Phase 4: Refactor
Apply changes file by file.
Template Changes
- Replace inline
style="..." with utility classes on the element.
- Add utility classes to elements where a scoped CSS rule can now be removed.
- Dynamic styles (
:style="computedValue") are acceptable — but check if the computed value could instead be a conditional class binding (:class="{ ... }").
Scoped Style Changes
- Remove rules now handled by utility classes.
- Replace all hard-coded values with CSS variables in remaining rules.
- Ensure every selector targeting a child component uses
:deep().
- If all scoped rules are eliminated, remove the entire
<style scoped> block.
Scoped Style Ordering
<style scoped>
.block-wrapper { ... }
.header-title { ... }
.is-active { ... }
:deep(.form-control) { ... }
</style>
Custom Class Hooks
Only add custom CSS class names on elements where theme or plugin authors would realistically want to customize appearance:
- Always: The outermost block wrapper div.
- Sometimes: Major structural sections (header, body, footer of a detail view), prominent visual elements (cards, badges, status indicators).
- Never: Internal layout divs that are purely structural, individual form fields, hidden elements.
Naming: [block-name]-[element] in kebab-case.
Examples: tag-list-header, prayer-request-card, step-participant-status.
Quality Gate
Before presenting changes, verify:
Troubleshooting
Dynamic styles that can't be replaced
Some :style bindings compute values at runtime (e.g., user-configured colors from the server). These cannot be replaced with utility classes. Keep the :style binding but extract any static portions into classes.
Utility class doesn't exist for this value
If a hard-coded value doesn't match any Rock spacing/color/typography token (e.g., width: 250px, max-height: 400px), keep it as a scoped style rule using the nearest CSS variable if one is close, or use the literal value with a comment explaining why no token fits.
Scoped style seems to do nothing
Before removing a scoped rule, verify it's truly unused. Vue scoped styles with :deep() target child component internals that may not be visible in the current file. Check the child component to confirm.
Block SCSS file not found
Not every block category has a _blocks-[category].scss file. If none exists, skip the block SCSS check and proceed with the audit using only Rock utilities and scoped styles.
Examples
Example 1: Inline style to utility classes
<div style="display: flex; gap: 8px; align-items: center">
<div class="d-flex gap-spacing-xs align-items-center">
Example 2: Scoped rule replaced by utilities
<div class="exception-header">
<style scoped>
.exception-header { display: flex; flex-direction: column; gap: 12px; }
</style>
<div class="d-flex flex-column gap-spacing-sm">
Example 3: Tokenize remaining scoped styles
.entity-picker :deep(.form-group label) {
margin-right: 8px;
font-size: 14px;
color: #8B8BA7;
}
.entity-picker :deep(.form-group label) {
margin-right: var(--spacing-xsmall);
font-size: var(--font-size-small);
color: var(--color-interface-medium);
}
Example 4: Bootstrap to Rock semantic
<div class="mb-3 p-2 bg-light text-muted">
<div class="mb-spacing-sm p-spacing-xs bg-interface-softer text-interface-medium">
Example 5: Dynamic style to class binding
<span :style="`background-color: ${statusColor}; color: #fff;`">
<span :class="statusClass" class="text-interface-softest">
<span :style="{ backgroundColor: statusColor }" class="text-interface-softest">