Asia/Dhaka
Case StudiesSeptember 27, 2025

CSS Architecture Refactoring in a WordPress Gutenberg Plugin

image
Three moves: the 2019 stylesheet blocked assigned feature work, one field type was extracted per commit across ~60 commits, and the result was 6,262 to 2,529 lines across 19 files.
I rebuilt the shared field-component stylesheet that every settings panel in PostX depends on, because the 2019-era monolithic system was blocking new feature work. The core stylesheet shrank from 6,262 lines to 2,529 lines by splitting it into 19 focused files - one per component. The work was self-proposed, executed solo across ~60 commits, completed September 2025, and left each field type independently editable - with affected build assets 20-30% smaller once the change was measured in isolation.
60% smaller core stylesheet, 19 independently editable component files, 13+ dependent files across the plugin.
ProblemA single 2019-era stylesheet controlled every UI field (toggles, color pickers, dropdowns, etc.) across the whole plugin. Editing one field type risked breaking others.
Who was affectedEvery developer touching any block's settings panel - 13+ files depended on this one system
What I didProposed and solo-executed a component-by-component rebuild, extracting each field's styles into its own file
OutcomeCore stylesheet shrank 60% (6,262 → 2,529 lines), split across 19 independently editable files
Bundle impactAffected assets 20-30% smaller, measured in isolation - an expected cost that didn't materialise
Developer experience19 independently editable files replaced one shared blast radius across 13+ dependents
TimelineCompleted September 2025
TeamSolo
Every Gutenberg block funnels through a settings panel built from shared field components, all of whose styling converged on one 6,262-line file from 2019.
PostX is a WordPress plugin for building news sites, blogs, and magazines using Gutenberg blocks - post grids, sliders, lists, and more. Every one of those blocks has a settings panel, and every settings panel is built from the same small set of shared field components: toggles, dropdowns, color pickers, typography controls, spacing sliders. Those components dated back to the plugin's original 2019 codebase. Over six years, their styling logic had all been funneled into one file. By the time I looked at it, that one file controlled the appearance of every field type used across the entire product. That's a fragile setup. Change a toggle's padding to fix one block, and you risk shifting a toggle somewhere else you didn't even open. I ran into this constraint while working on other features - it was actively slowing things down, not just a theoretical code smell.
Assigned feature work kept hitting the same shared-stylesheet friction; stopping to remove the constraint was the higher-leverage move, even unassigned.
I wasn't assigned this. I proposed it, because the shared component system had become a blocker for the feature work I was actually supposed to be doing. Fixing the constraint directly - rather than working around it again - seemed like the higher-leverage move, even though it meant putting other work down for a stretch. The full decision-making story is in the self-proposed engineering initiative case study →.
Three options compared on upfront cost and whether they fix the root cause: patch around it (low, no), adopt a library (medium, no fit), extract one component at a time (high, yes - chosen).
Option A - Patch around it. Keep adding to the monolithic file, be extra careful with regression testing each time. Fast short-term, but it doesn't remove the underlying risk, and the file only keeps growing. Option B - Adopt an existing design-system library. Would have saved build time, but PostX's field components are tightly coupled to WordPress's Gutenberg editor APIs. No strong candidate fit that constraint. Option C - Extract each component into its own file, one at a time. More work upfront, but it lets each component be understood, tested, and changed in isolation going forward. I picked Option C because it directly addressed the root cause - a shared blast radius - rather than managing around it.
Two-phase timeline across Jun-Jul and Sep 2025 with a two-month reassignment gap in between, treated as one continuous initiative, working through field types from Toggle to Accordion.
Over roughly 60 commits across two weeks in June–July 2025, I went through the shared component library one field type at a time: Toggle, Tab, Label, Range, Select, Dimension, Typography, Color, Multi-select, Accordion, and more. For each one, I pulled its styling out of the shared file and gave it its own dedicated partial. The work paused for about two months while I was reassigned to another project, then picked back up in September 2025 to finish the plan - extending the same approach into new controls (icons, border and alignment settings) as the plugin moved toward its next major version. I've treated this as one continuous initiative rather than two, because the September work was a direct continuation of the same plan, not a new decision. I kept the working branch in sync with the main codebase throughout, rather than letting it drift and risking a difficult merge at the end.
Before and after drawn to true scale: one 6,262-line editor.scss block against 19 partials totalling 2,374 lines, with editor.scss itself shrinking to 2,529 lines - a 60% reduction.
The clearest number: the core stylesheet (editor.scss) shrank from 6,262 lines to 2,529 lines - a 60% reduction - with that logic redistributed into 19 dedicated component files under a new editor_styles/ folder. Before: every field's styles lived inside one file.
reactjs/src/helper/
├── editor.scss              ← 6,262 lines, every field type mixed together
└── fields/
    ├── Toggle.js
    ├── Select.js
    ├── Range.js
    ├── Color.js
    ├── Typography.js
    ├── Dimension.js
    └── ... (component logic only, no dedicated styles)
After: each field owns its own stylesheet, imported into a much smaller editor.scss.
reactjs/src/helper/
├── editor.scss                       ← 2,529 lines, mostly @imports + shared rules
├── fields/
│   ├── Toggle.js
│   ├── Select.js
│   ├── Range.js
│   ├── Color.js
│   ├── Typography.js
│   ├── Dimension.js
│   └── ... (component logic - unchanged)
└── editor_styles/                    ← new folder, one file per field
    ├── _toggle.scss        (54 lines)
    ├── _select.scss        (303 lines)
    ├── _range.scss         (226 lines)
    ├── _color.scss         (274 lines)
    ├── _color2.scss        (138 lines)
    ├── _typography.scss    (447 lines)
    ├── _dimension.scss     (101 lines)
    ├── _accordion.scss     (119 lines)
    ├── _alignment.scss     (79 lines)
    ├── _icon.scss          (139 lines)
    ├── _label.scss         (21 lines)
    ├── _layout2.scss       (104 lines)
    ├── _linkButton.scss    (102 lines)
    ├── _number.scss        (24 lines)
    ├── _radioImage.scss    (55 lines)
    ├── _rowLayout.scss     (78 lines)
    ├── _section.scss       (38 lines)
    ├── _tags.scss          (54 lines)
    └── _divider.scss       (18 lines)
A representative slice of the ~60-commit series from the May linting commit through the Jun-Jul extraction run to the September tail, each commit named refactor: redesign [component].
A representative slice of the ~60-commit sequence, oldest to newest:
77d957187  feat: added wp js linter and formatter deps      (May 26)
7ffa69d75  refactor: redesign toggle component               (Jun 30)
343715883  refactor: redesign range component                (Jul  1)
0681dafae  refactor: redesign select component               (Jul  2)
9ba5e1770  refactor: multi select component                  (Jul  2)
9ab2c3e6e  refactor: redesign dimension component             (Jul  3)
53fde65a2  refactor: redesign color component                (Jul  3)
e43969ee4  refactor: redesign typography popup               (Jul  6)
f6f37589d  refactor: redesign typo field size                (Jul  6)
6b6d0eec1  refactor: redesign accordion tabs                 (Jul  7)
c20fb0dfb  refactor: change split scss files                 (Jul  8)
b7b1c3aee  refactor: redesign editor panel sidebar            (Jul  8)
...
943d70464  fix: alignment icons                               (Sep  2)
f570df086  feat: added border position icon                   (Sep  2)
91fdcd0e5  feat: added border radius position icon             (Sep  3)
deac0f96e  refactor: improve accordion section styling         (Sep  4)
One component per commit. The naming convention - refactor: redesign [component] - makes the series easy to follow and easy to bisect if anything went wrong.
Expected a larger bundle from per-file overhead, first measured growth that was confounded by unrelated image assets, then re-measured in isolation as 20-30% smaller.
Splitting one file into 19 generally means more per-file overhead and a larger total bundle. I expected that, and I said so before starting rather than discovering it after shipping - the alternative, keeping everything in one file to minimize size, was the same fragility that caused the problem in the first place. It didn't turn out that way. My first before/after build comparison did show growth, but the commit range I measured across also added new image assets, which inflated the "after" side independently of the CSS work. Re-measured in isolation, affected assets came in 20-30% smaller - extracting each component surfaced duplicated and dead rules that had been invisible inside a 6,262-line file. The measurement, what went wrong with the first one, and the developer-experience gains are in the bundle size case study →.
No automated test suite and 13+ dependent files, managed through component-by-component commits and manual verification - a substitute for tests, not equivalent to them.
PostX had no automated test suite. The system I was refactoring had 13+ dependent files. I managed that risk through careful, component-by-component commits and manual UI verification - but that's a substitute for tests, not equivalent to them. The full treatment of this is in the refactoring without a test suite case study →.
The three questions this case study answers: what problem it solved, whether it improved performance, and how regression risk was managed without a test suite.
What problem did this case study solve? A single legacy stylesheet controlled the appearance of every UI field (toggles, dropdowns, color pickers, etc.) used across a WordPress plugin's settings panels, making any change risky and slowing down feature work. Did this improve performance? Measured in isolation, affected build assets came in 20-30% smaller - extracting each component removed duplicated and dead rules. I had expected the opposite, and an early comparison appeared to confirm growth before I found it was confounded by unrelated image assets. The full measurement is in the bundle size case study →. How did you manage regression risk with no test suite? Component-by-component commits, continuous branch sync with main, and manual UI verification after each extraction. It worked - but I'd write test coverage for shared components before starting next time. Full detail in the refactoring without a test suite case study →.
Part of the PostX frontend refactoring case study series →

Related case studies

WordPress Plugin Frontend Refactoring - 5 Case Studies
October 9, 2025

WordPress Plugin Frontend Refactoring - 5 Case Studies

A self-proposed, solo-executed refactor of a WordPress Gutenberg plugin used by 60,000+ sites - five linked case studies covering the technical execution, the decision, the trade-offs, and the risk.
What a Self-Proposed Engineering Initiative Looks Like
October 5, 2025

What a Self-Proposed Engineering Initiative Looks Like

I noticed a legacy system blocking my feature work and fixed it without being asked - a case study in product-minded engineering judgment.