Summary
A developer managing a large table with ~100 dynamic highlight groups asked if CSS could match arbitrary class names via a capture group to avoid maintaining ~100 lines of selectors. The core issue is that CSS lacks pattern-matching capabilities for dynamic or arbitrary class names, and the :has() pseudo-class cannot select sibling elements based on a descendant’s dynamic class matching a pattern. The practical fix is to refactor the HTML to use a consistent data attribute (e.g., data-group="hg1") and leverage CSS attribute selectors combined with a small JavaScript helper to handle the hover propagation from any group cell to all matching cells, as pure CSS cannot solve this for arbitrary groups.
Root Cause
The root cause is a fundamental limitation in CSS architecture: selectors cannot perform dynamic string manipulation or regular expression matching.
- CSS is static: Selectors are evaluated at parse time against the DOM tree; there is no runtime “capture group” logic to extract a suffix like
hg1from a class string and reuse it in another selector. - :has() limitation: While
table:has(.hg1:hover) .hg1works for a fixed class, there is no selector liketable:has([class*="hg"]:hover) [class*="hg"]that can equate the matched string from the:hovertarget to theclassof other elements.:has()allows selecting a parent based on descendant conditions, but it cannot “pass” a matched value to another selector. - Selector explosion: To achieve “highlight all identical groups,” you must write one rule per group, leading to unmaintainable code.
Why This Happens in Real Systems
This anti-pattern arises frequently in systems where business logic evolves faster than the UI architecture:
- Data visualization tools: Tables rendering dynamic categories (e.g., product tags, status codes) where the grouping logic is determined by the backend data.
- Legacy migrations: Importing data into flat HTML tables without a frontend component framework to manage state.
- “No-JS” over-optimization: Attempting to solve complex interactive requirements with pure CSS leads to brittle, hardcoded selectors.
Real-World Impact
Maintaining hardcoded selectors for dynamic data creates severe operational friction:
- High maintenance burden: Every time a backend team adds, renames, or merges a group (e.g.,
hg101), the frontend developer must manually update the CSS file. - Risk of human error: Forgetting to update the CSS leads to UI bugs where new groups don’t highlight, confusing users.
- Scalability blocker: The solution is O(N) in terms of CSS file size relative to groups, which is unsustainable for hundreds of groups.
Example or Code
While there is no pure CSS solution for arbitrary groups, the proposed HTML refactor combined with a minimal script is the industry standard.
1. The HTML Refactor (Suggested by user)
Change from dynamic classes to a consistent data attribute:
Cell A
Cell B
Cell C
2. The Minimal JavaScript Helper
This script creates the “capture group” logic that CSS lacks. When hovering a cell, it finds all siblings with the same data-group and toggles a class.
document.querySelector('table').addEventListener('mouseover', (e) => {
if (e.target.matches('[data-group]')) {
const group = e.target.dataset.group;
// Select all cells in the table with this specific group
const targets = document.querySelectorAll(`[data-group="${group}"]`);
targets.forEach(t => t.classList.add('hover-highlight'));
}
});
document.querySelector('table').addEventListener('mouseout', (e) => {
if (e.target.matches('[data-group]')) {
const group = e.target.dataset.group;
const targets = document.querySelectorAll(`[data-group="${group}"]`);
targets.forEach(t => t.classList.remove('hover-highlight'));
}
});
3. The Supporting CSS
This CSS remains static and generic, regardless of how many groups exist.
.hover-highlight {
background-color: var(--highlight);
}
How Senior Engineers Fix It
Senior engineers recognize that complex UI state belongs in JavaScript, not CSS.
- Refactor HTML for Queryability: Ensure the element identifying the group is unique and queryable.
data-*attributes are the semantic standard for custom data. - Use Event Delegation: Instead of attaching listeners to hundreds of cells, attach a single listener to the
table(as shown in the code). - State Management: Use a class (
.hover-highlight) to represent the state, keeping styling separate from logic. - CSS Custom Properties: Define
--highlightat the root or table level for easy theming, but do not use CSS to calculate which elements to style.
Why Juniors Miss It
Junior developers often fall into this trap due to a strong desire to avoid JavaScript, which is often perceived as “heavier” or “more complex” than CSS.
- CSS-First Bias: They attempt to force CSS to do things it was never designed to do (string parsing, logic).
- Lack of Awareness: They may not be familiar with the
:has()pseudo-class limitations or how to use event delegation effectively. - Underestimating Maintenance: They might write 5 lines of CSS for the current requirements, not realizing that in 6 months, maintaining 100 lines of hardcoded values will be a nightmare.