Back to Css episodes

Css · Episode 1

CSS Architecture That Withstands Real-World Teams: Boundaries, Testing, and Maintainability

In this episode, we dive deep into the CSS architecture patterns that aren’t just popular in theory, but have actually survived the messy reality of real-world product teams. We explore how to draw meaningful boundaries within your CSS, tackle the persistent challenge of maintainability, and introduce practical testing strategies that prevent regressions even as teams and codebases scale. Through anonymized case studies and practical examples, we shine a light on the architectural decisions that turn CSS from a liability into a strength for modern teams. Listeners will leave with actionable strategies to prevent style sheet bloat, reduce cross-team friction, and ensure their CSS stands the test of time. Tune in for a grounded, experience-driven conversation about what works, what fails, and how to future-proof your styles.

HostMykyta R.Lead Software Engineer - Web, PHP and WordPress Platforms

GuestAlexandra Levesque — Lead Front-End Architect — Modular UI Solutions

CSS Architecture That Withstands Real-World Teams: Boundaries, Testing, and Maintainability

#1: CSS Architecture That Withstands Real-World Teams: Boundaries, Testing, and Maintainability

Original editorial from Softaims, published in a podcast-style layout—details, show notes, timestamps, and transcript—so the guidance is easy to scan and reference. The host is a developer from our verified network with experience in this stack; the full text is reviewed and edited for accuracy and clarity before it goes live.

Details

How codebase boundaries make or break CSS maintainability in large teams.

Common pitfalls and anti-patterns in real-world CSS architecture.

Techniques for writing testable CSS and catching regressions before they ship.

Balancing scalability with developer experience in modular CSS.

Case studies: How teams refactored or rescued legacy CSS.

Trade-offs between popular methodologies like BEM, ITCSS, and utility-first.

Establishing a shared CSS language to reduce cross-team confusion.

Show notes

  • The difference between theoretical and practical CSS architecture
  • Why boundaries matter in CSS, not just in code but in teams
  • How to spot CSS architecture problems early in a project
  • Introduction to BEM, ITCSS, and utility-first: pros and cons
  • How CSS boundaries help prevent style collisions
  • The role of naming conventions in maintainable CSS
  • Case study: Unraveling a tangled CSS codebase
  • Testing CSS: what does it mean and why it’s tricky
  • Visual regression testing and its place in real workflows
  • How to set up style linting and enforce standards
  • Dealing with legacy CSS: refactor or rewrite?
  • The impact of design systems on CSS architecture
  • How to communicate CSS boundaries across teams
  • When does CSS-in-JS make sense? When does it not?
  • Handling specificity wars and !important emergencies
  • Strategies for modularizing CSS in growing products
  • Onboarding developers to complex CSS architectures
  • Preventing and detecting unintended side effects
  • How to measure CSS maintainability (and why it’s hard)
  • Short-term hacks that become long-term headaches
  • What real-world teams wish they’d done sooner

Timestamps

  • 0:00Intro and episode overview
  • 2:10Meet Alexandra Levesque: background and experience
  • 4:00Why CSS architecture is a real-world challenge
  • 6:30Boundaries in CSS: what does it even mean?
  • 9:15First case study: Taming a legacy CSS jungle
  • 13:00Popular methodologies: BEM, ITCSS, utility-first
  • 16:30Where patterns fail: anti-patterns in teams
  • 19:45Naming conventions: more than just syntax
  • 21:00Mini disagreement: is BEM always the answer?
  • 22:30Trade-offs: developer experience vs. scalability
  • 25:00Testing CSS: what does it look like?
  • 27:30Visual regression tools and real-life gotchas
  • 30:00Second case study: Introducing boundaries to a fast-moving startup
  • 33:30How to sell the value of maintainable CSS to stakeholders
  • 36:30Design systems and cross-team collaboration
  • 39:30Legacy CSS: refactor, rewrite, or live with it?
  • 42:00Measuring maintainability: what matters?
  • 45:00Onboarding new devs to complex CSS architectures
  • 47:30Short-term hacks vs. long-term sustainability
  • 50:00Listener Q&A and final tips
  • 54:00Wrap-up and key takeaways

Transcript

[0:00]Mykyta: Welcome to the Modular Front-End podcast, where we dig into the building blocks of robust UI—one pattern at a time. I’m your host, Jamie Lin, and today we’re talking about something every front-end developer has wrestled with: CSS architecture that actually survives in real teams. Alexandra Levesque is here with me. Alexandra, welcome!

[0:23]Alexandra Levesque: Thanks, Jamie! I’m excited to be here. CSS can be a battleground in larger teams, so I’m glad we’re digging into the real-world side of things.

[0:38]Mykyta: Exactly. There’s so much theory out there about BEM, ITCSS, all these methodologies, but it rarely matches the messiness of a big, growing product. Before we dive in, can you give listeners a quick sense of your background?

[1:00]Alexandra Levesque: Sure! I’m a lead front-end architect at Modular UI Solutions. I’ve worked on everything from greenfield design systems to six-year-old e-commerce codebases where the CSS is older than most of the team. I help teams draw boundaries and build maintainable styles that don’t collapse under their own weight.

[1:25]Mykyta: I love that image—CSS collapsing under its own weight. That’s so real. Why do you think CSS architecture is such a recurring pain point for teams, even now?

[1:46]Alexandra Levesque: It’s mainly two things: first, CSS was designed to be global and forgiving, which is great for prototyping but dangerous as teams grow. Second, it’s invisible technical debt. You can ship a feature with messy CSS and it’ll work—for now. But over time, that mess slows everyone down.

[2:10]Mykyta: So true. And as soon as you have more than a handful of people touching the same files, chaos sneaks in. Let’s start at the top: when you say 'boundaries' in CSS, what do you mean?

[2:35]Alexandra Levesque: Boundaries are about drawing lines—deciding where one part of your UI’s styles end and another begins. It can be as simple as keeping layout and component styles separate, or as formal as using a methodology like BEM to give every class a clear home. Without boundaries, changes leak everywhere.

[2:58]Mykyta: I’ve seen teams try to do this with folders, or with naming conventions, or just by writing everything in one big file and hoping for the best. Does it matter how you do it, or just that you do it?

[3:22]Alexandra Levesque: It matters. If your boundaries aren’t visible to everyone—if they live only in someone’s head—they’ll get crossed. Naming conventions help, folder structure helps, but you need a shared mental model. Otherwise, one person’s 'header' is another’s 'main navigation', and styles start bleeding together.

[3:41]Mykyta: Let’s pause and define that: 'style bleeding'. What does it look like in a codebase?

[3:58]Alexandra Levesque: Style bleeding is when changes to one part of the app accidentally affect others. Maybe you update a button’s padding for a new modal, and suddenly buttons everywhere look weird. That’s usually because there’s no clear boundary—global selectors, too-broad class names, or just unclear ownership.

[4:20]Mykyta: Great example. I think a lot of people listening have had that Friday afternoon, 'why did the footer break on the homepage?' moment. Can you walk us through a concrete example—maybe a project where boundaries were missing and what it took to fix it?

[4:44]Alexandra Levesque: Absolutely. We worked with a fintech team whose CSS was all in one massive file. No structure, just thousands of lines. If someone changed a color variable, it’d affect forms, charts, emails—everywhere. The fix started with mapping out UI regions, then splitting styles by responsibility. We introduced BEM, set up clear folders, and every component got its own stylesheet. It took months, but bugs dropped dramatically.

[5:18]Mykyta: That must have been painful at first. Did the team push back?

[5:34]Alexandra Levesque: For sure. Some devs thought it was 'just extra work'. But when they saw how much easier it was to find and fix bugs—or even just answer 'where should I put this style?'—they came around. The real win was onboarding: new hires could contribute safely.

[5:55]Mykyta: You mentioned BEM. For listeners who aren’t familiar, can you give the elevator pitch—and how does it help with boundaries?

[6:15]Alexandra Levesque: BEM stands for Block, Element, Modifier. It’s a naming convention where you break UI into blocks (like 'card'), elements inside blocks (like 'card__title'), and modifiers for state (like 'card--highlighted'). It makes it clear what each class is for, so styles only apply where intended.

[6:38]Mykyta: That clarity really helps. But I’ve also seen teams try utility-first CSS—where you use lots of small, single-purpose classes. Is that a different kind of boundary?

[6:59]Alexandra Levesque: It is. Utility-first—like with Tailwind—puts boundaries right in the markup. Each class does one thing, so you rarely end up with mysterious side effects. But it can get verbose, and not everyone loves reading HTML with twenty classes on every element.

[7:18]Mykyta: Let’s dig into that. In your experience, what’s the main trade-off between a BEM-like approach and utility-first CSS?

[7:40]Alexandra Levesque: With BEM, you keep design intent in the CSS. It’s readable and good for collaboration, but you have to maintain the stylesheets. Utility-first flips that: you move intent to the HTML, which can be faster for prototyping and consistent, but harder to manage in big, long-lived apps if you overdo it. Both can work—what matters is consistency and team buy-in.

[8:04]Mykyta: Let’s talk anti-patterns. What are the classic mistakes you see teams make, regardless of methodology?

[8:25]Alexandra Levesque: Number one: global selectors, like styling all 'h1' tags without context. Number two: overusing '!important' to solve specificity wars. Third is copy-pasting styles instead of reusing components. And last, mixing concerns—putting layout, colors, and animation all in one file with no separation.

[8:48]Mykyta: Specificity wars—I love that phrase. Can you give a quick example?

[9:03]Alexandra Levesque: Sure. Suppose someone adds a '.button' class, but later another dev wants a special button for the header and writes '.header .button'. Now those styles are fighting each other. To win, someone adds '!important'. Suddenly, you need three levels of specificity just to make a blue button, and nobody knows what’s safe to change.

[9:28]Mykyta: It’s like an arms race—each dev escalating until you need a chart to understand what color your buttons even are. How do you get teams out of that cycle?

[9:44]Alexandra Levesque: You have to reset. Audit where specificity is getting out of hand and refactor. That usually means scoping styles more tightly—using BEM classes or utilities—instead of relying on tag or context selectors. Linting tools can help spot bad patterns before they grow.

[10:05]Mykyta: Let’s bring in a case study. Can you share a story where a team had to untangle a legacy CSS mess?

[10:28]Alexandra Levesque: Definitely. One product team I worked with had grown from three to fifteen engineers in under a year. Their initial CSS was fine for a small site, but as the team grew, styles overlapped, and fixes for one bug broke something else. They paused feature work for a sprint and mapped every global style, then refactored into component-based stylesheets—using BEM for naming. It was a slog, but it paid off: bug reports fell, and deploying new features became less scary.

[11:05]Mykyta: That’s a big investment. Was there a moment where you thought, 'This might not work?'

[11:23]Alexandra Levesque: Oh, absolutely. Midway through, there were regressions—styles missing, layouts breaking. But we’d set up visual regression tests to catch those issues. That gave us the confidence to keep going. Afterwards, the team said they wished they’d started sooner.

[11:40]Mykyta: Visual regression tests—let’s pause and break that down. What are those, and how do they help with CSS?

[11:58]Alexandra Levesque: Visual regression testing means taking screenshots of your UI before and after changes, and highlighting any differences. Tools like Percy or Chromatic automate this. It’s great for catching layout bugs that code review might miss, especially when refactoring lots of CSS.

[12:18]Mykyta: So instead of just testing logic, you’re literally testing pixels—you can see if a button shifted or a color changed unexpectedly.

[12:30]Alexandra Levesque: Exactly. It’s not perfect—you get some false positives with dynamic content—but it’s a huge safety net. Especially when teams are nervous about touching old styles.

[12:45]Mykyta: Let’s shift gears. We talked about BEM and utility-first. Where does ITCSS fit in?

[13:03]Alexandra Levesque: ITCSS stands for Inverted Triangle CSS. It’s about organizing your styles from the most generic (like resets) to the most specific (like component tweaks). That order helps manage specificity and keeps overrides predictable. It’s a great fit for design systems and larger apps.

[13:25]Mykyta: So, with ITCSS, you’re setting up layers—like settings, tools, generic, elements, objects, components, and trumps. Is that right?

[13:39]Alexandra Levesque: Exactly. Each layer has a purpose. For example, 'trumps' are utilities or overrides that trump everything else—hence the name. By being explicit about where styles belong, you avoid accidental overrides.

[13:55]Mykyta: But is it too heavyweight for smaller teams? I’ve heard some folks say it’s overkill.

[14:10]Alexandra Levesque: It can be. If you’re shipping a landing page, ITCSS is probably too much. But if you have a growing team and lots of shared components, that structure pays dividends. Like any pattern, it’s about matching the tool to the problem.

[14:28]Mykyta: Let’s do a quick recap for listeners: We’ve got BEM, utility-first, and ITCSS as three ways to draw boundaries. Is there one you’d recommend for most teams?

[14:43]Alexandra Levesque: I’ll be honest: no one size fits all. BEM is great for shared ownership, utility-first is awesome for rapid UI work, and ITCSS shines in complex systems. What matters is picking one, documenting it, and sticking with it. Consistency beats perfection.

[15:00]Mykyta: That’s a good segue to the next topic: documentation. How do you help teams make their CSS boundaries obvious to everyone, not just the original architect?

[15:20]Alexandra Levesque: Start with a style guide—even a basic one. Include naming conventions, folder structure, and examples. Code comments help. But the best way is code review: have team members explain their choices. That’s how shared language gets built.

[15:38]Mykyta: And what about linting? Do you see teams use stylelint or similar tools to enforce rules?

[15:53]Alexandra Levesque: Absolutely. stylelint can catch things like duplicate selectors, unused classes, or specificity issues. It’s like a spellchecker for your CSS. Teams that use it consistently have fewer surprises.

[16:09]Mykyta: Let’s talk about mistakes. What’s a common anti-pattern you still see even with tools like stylelint in place?

[16:25]Alexandra Levesque: The biggest is bypassing the rules—adding '!important' or inline styles to 'fix' something fast. Those hacks add up. Another is not removing dead code—old classes stick around, cluttering the stylesheet.

[16:45]Mykyta: Dead code is sneaky. It doesn’t break anything, but it makes the codebase harder to navigate. How do you encourage teams to clean it up?

[17:00]Alexandra Levesque: Schedule regular CSS cleanups—maybe every sprint or release. Tools like PurgeCSS can help find unused classes. And celebrate when someone deletes code! Less is often more.

[17:20]Mykyta: Let’s introduce a little controversy. Some folks swear by BEM, others find it verbose or restrictive. Alexandra, do you ever recommend skipping BEM?

[17:40]Alexandra Levesque: I think BEM is great for teams, but it’s not the only way. If your team loves utility classes and can keep things consistent, that works too. I’d rather see a team stick to a simple, well-understood pattern than force BEM and end up with confusion and resentment.

[17:55]Mykyta: So you’re not a BEM absolutist!

[18:05]Alexandra Levesque: Not at all. It’s a tool, not a religion. The real danger is mixing patterns—half the codebase BEM, half utility, half random. That’s where confusion and bugs thrive.

[18:22]Mykyta: That’s a great point. Let’s get into developer experience. Sometimes strict patterns slow people down, especially in the short term. How do you balance maintainability with letting devs move quickly?

[18:44]Alexandra Levesque: It’s a balance for sure. If rules are too rigid, people will find ways around them. I like to define a 'happy path'—the recommended way to structure styles—but also allow for exceptions with a clear process. That way, devs feel empowered, not boxed in.

[19:05]Mykyta: Let’s talk about testing. We touched on visual regression. What other kinds of CSS testing do you see in the wild?

[19:22]Alexandra Levesque: Snapshot testing is one—where you take a snapshot of a rendered component’s output and compare it over time. Another is style linting, which we mentioned. And in some teams, we do integration testing—making sure a component renders with the right classes and styles as part of the test suite.

[19:41]Mykyta: Do you ever see teams write unit tests for CSS? Or is that overkill?

[19:57]Alexandra Levesque: Unit testing CSS is rare and usually not worth it. CSS is about appearance, which is hard to test in isolation. But integration tests—verifying that a button is red when it should be, or that a modal is hidden by default—those are valuable.

[20:12]Mykyta: Let’s do a lightning round: What’s the most underrated CSS tool for maintainability?

[20:22]Alexandra Levesque: For me, it’s stylelint. It’s easy to set up, customizable, and catches so many issues before code review.

[20:29]Mykyta: And most overrated?

[20:36]Alexandra Levesque: I’d say CSS-in-JS, if used without clear boundaries. It promises isolation but can just move the mess around if you’re not careful.

[20:47]Mykyta: Let’s unpack that a bit. CSS-in-JS has been hyped for isolation, but you’ve seen it go wrong?

[21:00]Alexandra Levesque: Definitely. If teams mix CSS-in-JS with global styles, or don’t document shared variables, you end up with confusion. Also, bundle size can balloon if you’re not careful. It’s not a silver bullet.

[21:18]Mykyta: That’s a good warning. Let’s go back to boundaries for a minute. How do you actually enforce them in code reviews, beyond just hoping people remember?

[21:35]Alexandra Levesque: Have a checklist—are components using only their own styles? Are there any global selectors? And require explanations when someone breaks a rule. Over time, it becomes second nature.

[21:49]Mykyta: Do you see value in pairing developers for CSS-heavy tasks?

[22:00]Alexandra Levesque: Absolutely. CSS is so visual and context-dependent that pairing helps catch issues early. Plus, it spreads architectural knowledge across the team.

[22:13]Mykyta: Let’s bring in another anonymized case study—a startup with a fast-moving product. How did you introduce CSS boundaries without slowing them down?

[22:33]Alexandra Levesque: This was a SaaS dashboard team, five engineers, shipping weekly. Their CSS was all inline or in big global files. We started by extracting just the button and input styles into their own files, with clear BEM classes. No big bang rewrite, just a gradual process. After a month, they had patterns to copy, and new features always started with a dedicated stylesheet.

[22:59]Mykyta: So it wasn’t about stopping everything to refactor, but making each new feature a little better than the last?

[23:07]Alexandra Levesque: Exactly. Small wins, no big scary rewrites. That’s what survives in real teams.

[23:18]Mykyta: Let’s talk about onboarding. When new devs join, how do you help them understand CSS boundaries and testing strategies?

[23:34]Alexandra Levesque: We pair them with someone who knows the system and walk through a real bug fix. Seeing the boundaries in action is more powerful than any document. And we highlight our testing tools—showing how a style change triggers a visual regression test.

[23:52]Mykyta: That’s great. Have you ever run into a situation where a new hire accidentally broke boundaries? How did you handle it?

[24:08]Alexandra Levesque: Oh, definitely. Someone once added a global '.active' class that clashed with half a dozen components. We caught it in code review, explained why it was risky, and paired them on a better solution. The important thing is to treat it as a learning moment, not a failure.

[24:28]Mykyta: That’s a healthy approach. Let’s circle back to maintainability. What metrics or signs do you look for to know your CSS is healthy?

[24:45]Alexandra Levesque: Number of bugs traced to CSS, time spent onboarding, and how often you need '!important' are all clues. Also, how easy it is to remove a component without breaking things. If it’s a nightmare, your boundaries need work.

[25:00]Mykyta: Quick reality check: Is it possible to have zero tech debt in CSS?

[25:13]Alexandra Levesque: Nope! Styles are always evolving. The goal is to manage debt, not eliminate it. If you’re shipping fast and learning, some mess is natural. But you want a process for cleaning up before it gets out of hand.

[25:28]Mykyta: Let’s wrap this segment by talking about testing again. For a team just starting out, what’s the minimum viable CSS testing setup?

[25:43]Alexandra Levesque: Start with linting and code review. Add visual regression tests for critical components. Even a manual checklist helps. The key is to catch regressions before users do.

[25:49]Mykyta: And as the team grows?

[26:00]Alexandra Levesque: Automate as much as possible. Add snapshot or integration tests. Document test coverage in your PRs. And keep evangelizing good patterns—testing is everyone’s job.

[26:13]Mykyta: Okay, last question before we move into more advanced strategies: What’s the biggest CSS testing mistake you see teams make?

[26:23]Alexandra Levesque: Relying only on manual testing. Humans miss things—especially in big apps. Automation isn’t perfect, but it scales better.

[26:35]Mykyta: That’s a great place to pause. When we come back, we’ll talk about tools for visual regression, metrics for maintainable CSS, and what to do with legacy styles you can’t just delete. Stay with us.

[26:46]Alexandra Levesque: Looking forward to it!

[26:55]Mykyta: And if you’re listening and have a CSS horror story or a question about boundaries, drop us a message. We might feature it later in the episode.

[27:05]Alexandra Levesque: I’d love to hear those! Some of the best lessons come from war stories.

[27:15]Mykyta: Alright, quick break, and then we’ll dig into visual regression tools and how to measure whether your CSS is actually maintainable. Don’t go anywhere.

[27:30]Mykyta: Alright, we’re back! We’ve already unpacked some fundamentals of CSS architecture, but let’s dig even deeper into boundaries, testing, and what really survives in actual team environments. I want to pivot a bit—what do you see as the most common way boundaries break down in real projects?

[27:50]Alexandra Levesque: Yeah, the classic scenario is when boundaries are defined too loosely, or not enforced at all. Teams might start with good intentions, like using BEM or CSS Modules, but as deadlines creep up, people cut corners. Suddenly, you see styles leaking between components, globals being reintroduced, and specificity wars. It’s rarely malicious—just lack of process and pressure to ship.

[28:17]Mykyta: Totally. I’ve seen that too. Can you share a specific example where this happened and what the consequences were?

[28:30]Alexandra Levesque: Sure! There was a SaaS dashboard I worked on. The team started off with a strict CSS-in-JS setup, but as more devs joined, we noticed utility classes from one module bleeding into another. Eventually, styling a simple button would break a modal somewhere else. Debugging was a nightmare, because the boundaries we thought we had weren’t real anymore.

[28:58]Mykyta: That’s such a relatable pain. Did you fix it, or was it just a band-aid job after that?

[29:10]Alexandra Levesque: We actually paused feature work for a sprint and rewrote a chunk of the CSS, moving to stricter encapsulation. We also automated linting for style boundaries. It was expensive short-term, but long-term, it saved us from constant regressions.

[29:35]Mykyta: That’s a great segue to testing. CSS isn’t typically thought of as ‘testable’ in the same way as JavaScript. How do you approach CSS testing in a maintainable architecture?

[29:55]Alexandra Levesque: Visual regression testing is key. Tools like Percy or Chromatic can snapshot your UI and catch unexpected changes. Also, style linting and even unit tests for style functions if you use CSS-in-JS. But the overarching thing is: automate what you can, and make visual review part of your pull request process.

[30:17]Mykyta: What about teams that don’t have the luxury of those tools, maybe smaller teams? What’s the minimum viable approach to CSS testing?

[30:32]Alexandra Levesque: A lightweight approach is to use stylelint with custom rules, and enforce that every major component has a visual test page. Even manual screenshot comparisons can go a long way if you’re systematic. The key is consistency—don’t skip reviews just because it’s ‘just CSS’.

[30:55]Mykyta: Love that. You mentioned specificity wars earlier. Can we talk about specificity and how teams get tripped up by it?

[31:10]Alexandra Levesque: Definitely. Specificity issues often creep in when teams start overriding styles without understanding the cascade. You end up with !important everywhere, or selectors that are overly specific. It’s a sign your architecture isn’t holding up. The fix is to define clear layering—globals, base, components, utilities—and stick to it. And educate the team about how specificity works.

[31:38]Mykyta: Can you give an example where specificity went wild?

[31:49]Alexandra Levesque: Absolutely. On one ecommerce platform, we had three or four layers of overrides for button styles. Each new feature added its own ‘fix’. Eventually, the only way to make a button red was to add a class with four parent selectors and !important. It got so bad that changing a button color could break the checkout flow.

[32:16]Mykyta: Yikes—that sounds rough. Did you re-architect, or just live with it?

[32:26]Alexandra Levesque: We re-architected. We introduced a utility-first approach, similar to Tailwind, and drew strict boundaries. It was a big lift, but after a few weeks, we had way fewer bugs and onboarding new devs was easier.

[32:48]Mykyta: That transitions nicely into maintainability. What are signs that CSS is becoming unmaintainable?

[33:00]Alexandra Levesque: If you dread touching the styles, that’s a sign. Also, if onboarding new devs takes longer than it should, or if you’re seeing repeated regressions in unrelated areas. Another red flag is when you need to grep the whole codebase to find what’s affecting a component’s look.

[33:20]Mykyta: So, let’s talk about boundaries again. Are there any patterns you think are overrated—or even dangerous—in real teams?

[33:33]Alexandra Levesque: Atomic CSS is powerful, but it can get out of hand if you don’t pair it with conventions and documentation. Similarly, global scope patterns—like dumping everything into one stylesheet—are a recipe for pain. The most dangerous thing is not having any pattern at all.

[33:55]Mykyta: Do you have a favorite architecture pattern for medium-sized teams?

[34:08]Alexandra Levesque: For most teams, I recommend CSS Modules or a variant of BEM with strict linting. It gives you enough structure without being too rigid. For larger scale or design systems, CSS-in-JS can be a win, but only if the team is ready for the overhead.

[34:30]Mykyta: Let’s get practical. Imagine a team just inherited a legacy codebase with thousands of lines of spaghetti CSS. Where do they start?

[34:44]Alexandra Levesque: First, resist the urge to rewrite everything. Start by mapping out what’s global and what’s local. Introduce a linter, and slowly migrate components as you touch them. Add documentation as you go. If you can, set up visual regression testing early, so you can refactor with confidence.

[35:08]Mykyta: That’s super helpful. Let’s do a quick rapid-fire round! Ready?

[35:12]Alexandra Levesque: Let’s do it!

[35:15]Mykyta: Inline styles: ever okay in production?

[35:18]Alexandra Levesque: For dynamic styles or one-off tweaks, yes. But not for core layout or theming.

[35:22]Mykyta: Global resets: must-have or overrated?

[35:25]Alexandra Levesque: Must-have, but keep them minimal and predictable.

[35:29]Mykyta: CSS variables or preprocessor variables?

[35:31]Alexandra Levesque: CSS variables—more dynamic, easier to theme.

[35:34]Mykyta: Single-file CSS or split by component?

[35:36]Alexandra Levesque: Split by component, always.

[35:39]Mykyta: !important: ever allowed?

[35:41]Alexandra Levesque: Only if you’re fixing a third-party widget. Otherwise, it’s a red flag.

[35:45]Mykyta: Manual testing or automated visual regression?

[35:48]Alexandra Levesque: Automated, but always double-check key flows manually before releases.

[35:54]Mykyta: Love it! Back to the trenches—let’s do another mini-case study. Can you share a story about a team that nailed CSS maintainability?

[36:04]Alexandra Levesque: Absolutely. There was a fintech company that scaled from 4 to 20 devs in a year. They invested early in a design system and used CSS Modules with a custom stylelint config. Every new component went through a style review, and visual diffs were mandatory for pull requests. Even as they scaled, regressions were rare and onboarding was smooth.

[36:31]Mykyta: What did they do differently than most teams?

[36:42]Alexandra Levesque: Consistency and documentation. They wrote down their architecture decisions and stuck to them. Also, they empowered designers and developers to collaborate on components, not just hand off specs.

[37:01]Mykyta: That collaboration piece is so underrated. How do you recommend teams encourage it?

[37:13]Alexandra Levesque: Regular design-dev syncs, and making sure designers understand the limitations and strengths of the CSS stack. Pairing sessions can be amazing—just an hour together can save days of frustration.

[37:32]Mykyta: Let’s step back. In your experience, what’s the hardest part of maintaining boundaries as teams grow?

[37:43]Alexandra Levesque: It’s communication. As teams grow, not everyone is on the same page about conventions or why boundaries matter. You need documentation, code reviews, and someone responsible for architectural consistency.

[38:00]Mykyta: How do you make sure those boundaries actually survive over time?

[38:12]Alexandra Levesque: Automate as much as possible—linting, testing, CI checks. But also, have regular architecture retros to revisit decisions. Teams change, and your CSS architecture should evolve with them, not just gather dust.

[38:31]Mykyta: Have you ever seen an architecture decision that looked great on paper but totally failed in production?

[38:43]Alexandra Levesque: Definitely. I saw one team go all-in on a purely functional CSS library. It worked for a while, but when designers wanted more creative layouts, the system became too rigid. They ended up fighting their own tools.

[39:02]Mykyta: So, how do you balance flexibility for designers with maintainability for devs?

[39:15]Alexandra Levesque: You set guardrails, not walls. Give designers enough tokens and utilities to be creative, but document how to extend the system. And be open to iterating on the architecture as needs change.

[39:33]Mykyta: Let’s talk about testing in CI. What’s the minimum you’d automate for CSS before merging a pull request?

[39:46]Alexandra Levesque: At minimum: run stylelint, check for duplicate selectors, and visual regression tests for core pages. If you have a design system, snapshot tests are great for catching breaking changes.

[40:05]Mykyta: And what about documentation? What’s the sweet spot for documenting CSS architecture?

[40:17]Alexandra Levesque: Enough so a new team member can onboard in a day or two. Document naming conventions, component boundaries, theming, and how to add new components. Keep it living—update docs as you go.

[40:37]Mykyta: Let’s do one more case study. Have you seen a team recover from a total CSS meltdown?

[40:49]Alexandra Levesque: Yes! A startup I worked with had no CSS conventions, and stylesheets were thousands of lines long. Bugs everywhere. They brought in a style guide and used CSS Modules to break things up. It took a few months, but eventually, they shipped faster and had way fewer bugs.

[41:15]Mykyta: What was their biggest challenge during that recovery?

[41:27]Alexandra Levesque: Convincing leadership to invest the time. Refactoring CSS isn’t glamorous—everyone wants new features. But once they saw fewer production bugs, it paid off.

[41:46]Mykyta: Let’s talk trade-offs. When would you actually recommend using global CSS?

[41:58]Alexandra Levesque: For true global styles—like font faces, base resets, or theming variables. But keep global rules as thin as possible. The rest should live in components.

[42:17]Mykyta: What about theming? Any gotchas with CSS variables or themes in real teams?

[42:29]Alexandra Levesque: Scope your variables. If everything is global, it’s hard to manage. Use CSS custom properties for themes, but encapsulate them—like scoping to a root element for dark mode. And always document how themes are supposed to work.

[42:50]Mykyta: How do you prevent regressions as a codebase grows?

[43:01]Alexandra Levesque: Automated visual regression is your friend. Also, enforce code review for styles, not just logic. And have a changelog for your design tokens or variables.

[43:20]Mykyta: Let’s dig into onboarding. What’s the first thing you show a new dev about your CSS setup?

[43:32]Alexandra Levesque: I walk them through the file structure, the naming conventions, and how to run the linter and tests. I also show a real example: here’s how you add a new button, here’s how to theme it.

[43:54]Mykyta: Have you ever disagreed with a team about CSS architecture? How do you resolve it?

[44:06]Alexandra Levesque: Oh, all the time! The best way is to prototype both approaches, then weigh pros and cons as a team. Data from visual regressions or onboarding times can help make the case, rather than opinions.

[44:24]Mykyta: Let’s get into some anti-patterns. What’s a CSS architecture anti-pattern you wish would disappear?

[44:36]Alexandra Levesque: Overusing !important, relying on long selector chains, and dumping random utility classes into global scope. Those three cause more pain than they ever solve.

[44:54]Mykyta: Alright, as we get close to wrapping up, I want to do an implementation checklist. Can you walk us through the key steps for introducing a maintainable CSS architecture in a real team?

[45:06]Alexandra Levesque: Absolutely! Let’s break it down step by step:

[45:10]Alexandra Levesque: Step one: Audit your current CSS. Figure out what’s global, what’s component, and what’s just dead code.

[45:24]Alexandra Levesque: Step two: Pick an architecture pattern—BEM, CSS Modules, CSS-in-JS, whatever fits your team and stack.

[45:36]Alexandra Levesque: Step three: Set up linting and automated checks. Stylelint is a great start.

[45:45]Alexandra Levesque: Step four: Agree on naming conventions and document them. Make sure everyone knows how to add new components.

[45:53]Alexandra Levesque: Step five: Set up visual regression testing, even if it’s manual screenshots at first.

[46:02]Alexandra Levesque: Step six: Start migrating components as you touch them. Don’t try to rewrite everything overnight.

[46:13]Alexandra Levesque: Step seven: Hold regular check-ins to review what’s working and what’s not. Refactor your process if needed.

[46:27]Mykyta: That’s gold. Anything to add on rollout or change management?

[46:36]Alexandra Levesque: Communicate early and often! Get buy-in from designers and leadership. Celebrate quick wins, like reducing regressions or faster onboarding. And be patient—real change takes time.

[46:55]Mykyta: Before we wrap, what’s one mistake you want every listener to avoid when it comes to CSS architecture?

[47:05]Alexandra Levesque: Don’t ignore the process. The biggest mistake is thinking CSS isn’t ‘real code’ or doesn’t need structure. Treat it like any other piece of your stack.

[47:22]Mykyta: What’s one resource you’d recommend for teams wanting to dive deeper?

[47:32]Alexandra Levesque: Check out open-source design systems and their documentation—they’re a goldmine. Also, the docs for stylelint and visual regression tools are super practical.

[47:49]Mykyta: Any closing thoughts for folks working on CSS in real teams today?

[48:01]Alexandra Levesque: CSS is a team sport. Invest in communication, automate what you can, and don’t be afraid to evolve your architecture as you learn. It’s not about perfection—it’s about building something that lasts.

[48:20]Mykyta: I love that. Let’s recap with a final checklist for listeners who want to improve their CSS architecture starting today. Ready?

[48:23]Alexandra Levesque: Ready!

[48:25]Mykyta: Alright, here’s the shortlist:

[48:28]Mykyta: 1. Audit your current CSS and spot pain points.

[48:32]Mykyta: 2. Pick one clear architecture pattern—don’t mix and match randomly.

[48:35]Mykyta: 3. Set up linting and automated style checks.

[48:38]Mykyta: 4. Agree and document naming conventions and boundaries.

[48:42]Mykyta: 5. Start small—migrate as you touch components.

[48:45]Mykyta: 6. Use visual regression testing to catch unintentional changes.

[48:48]Mykyta: 7. Hold regular retros to keep your architecture healthy.

[48:52]Alexandra Levesque: And I’d add: keep the conversation going between devs and designers, and celebrate the small wins!

[48:58]Mykyta: Perfect! Thanks so much for joining and sharing all this wisdom.

[49:05]Alexandra Levesque: Thanks for having me—it was a pleasure!

[49:15]Mykyta: Alright folks, that’s it for today’s episode of Softaims. Quick reminder: you can find more resources and show notes on our site.

[49:27]Mykyta: If you enjoyed this episode, leave us a review, subscribe, and let us know what CSS challenges you’re facing.

[49:36]Mykyta: Thanks for listening, and we’ll see you next time!

[49:40]Alexandra Levesque: Take care and happy styling!

[49:45]Mykyta: That’s a wrap. Good luck out there—may your styles stay scoped and your regressions few.

[49:52]Mykyta: Signing off from Softaims. Until next time!

[49:55]Alexandra Levesque: Bye!

[50:00]Mykyta: And we’re out.

[50:03]Mykyta: [Outro music fades in]

[50:10]Mykyta: You’ve been listening to Softaims. For transcripts, resources, and more, visit our website.

[50:18]Mykyta: Special thanks to our guest and everyone behind the scenes. Stay tuned for the next episode.

[50:25]Mykyta: [Music continues]

[50:32]Mykyta: This episode was produced by the Softaims team. If you have feedback or want to suggest a topic, reach out to us on social or our contact page.

[50:40]Mykyta: We appreciate you. Have a great day!

[55:00]Mykyta: [Music fades out]

More css Episodes