csscade


Namecsscade JSON
Version 0.3.0 PyPI version JSON
download
home_pageNone
SummaryIntelligent CSS merging with conflict resolution and multiple strategies
upload_time2025-09-10 13:53:51
maintainersebieire
docs_urlNone
authorsebieire
requires_python>=3.7
licenseMIT
keywords css merge conflict-resolution styling theme customization
VCS
bugtrack_url
requirements cssutils
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # CSSCade 🎨

[![PyPI version](https://img.shields.io/pypi/v/csscade.svg)](https://pypi.org/project/csscade/)
[![Python Support](https://img.shields.io/pypi/pyversions/csscade.svg)](https://pypi.org/project/csscade/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

**Intelligent CSS merging and conflict resolution with two powerful systems**

CSSCade provides two complementary systems for CSS manipulation:

1. **CSSMerger** - Property-level CSS merging with multiple strategies
2. **Combinator** (v0.3.0) - Intelligent conflict detection for existing CSS frameworks

Perfect for theme customization, CSS framework overrides, runtime CSS manipulation, and CSS-in-JS implementations.

I was looking for something like this in the python ecosystem and couldn't find anything that matched my usecase so here it is. Enjoy!

## Features ✨

### Core Features
- **Intelligent Conflict Resolution**: Handle !important, shorthand properties, and duplicates
- **Pseudo-selector Support**: Full support for :hover, :focus, :before, :after, etc.
- **Media Query Support**: Responsive design support with @media rules
- **Production Ready**: Used in real-world applications for dynamic theming

### CSSMerger System
- **3 Merge Modes**: Permanent, Component, and Replace strategies
- **Multi-Rule Support**: Process all CSS rules including pseudo-selectors with `rule_selection='all'`
- **Selective Application**: Target specific rules with the `apply_to` parameter
- **Smart Property Merging**: Configurable shorthand strategies (cascade, smart, expand)
- **CSS Validation**: Optional property and value validation with helpful warnings
- **Flexible Naming**: Multiple class naming strategies (semantic, hash, sequential)

### Combinator System (v0.3.0)
- **Automatic Conflict Detection**: Analyzes existing CSS classes and finds conflicts
- **Framework Integration**: Works with Bootstrap, Tailwind, or any CSS framework
- **Smart Class Management**: Automatically determines which classes to keep/remove
- **Override Generation**: Creates optimized override CSS with minimal !important usage
- **Inline Fallback**: Generates React/JS-compatible inline styles
- **Batch Processing**: Efficiently process multiple elements

## Installation

```bash
pip install csscade
```

**Minimal Dependencies:** CSSCade has only ONE runtime dependency (`cssutils`) making it lightweight and fast to install!

## Quick Start 🚀

CSSCade offers two approaches depending on your needs:

### Method 1: CSSMerger - For Manual CSS Generation

```python
from csscade import CSSMerger

# Create a merger (defaults to 'component' mode)
merger = CSSMerger()

# Merge CSS
source_css = ".btn { color: red; padding: 10px; }"
overrides = {"color": "blue", "margin": "5px"}

result = merger.merge(source_css, overrides)

# Result structure:
print(result['css'])        # ['.csscade-btn-3f4a { color: blue; padding: 10px; margin: 5px; }']
print(result['add'])        # ['csscade-btn-3f4a']
print(result['preserve'])   # ['btn']
```

### Method 2: Combinator - For Working with Existing CSS Frameworks

```python
from csscade.combinator import Combinator

# Load your CSS framework
combinator = Combinator()
combinator.load_css(['bootstrap.css', 'custom.css'])

# Process an element with conflicts
result = combinator.process(
    element_classes=['btn', 'btn-primary', 'p-3'],
    overrides={
        'background': '#28a745',
        'padding': '2rem',
        ':hover': {
            'background': '#218838'
        }
    },
    element_id='my_button'
)

# Result structure:
print(result['remove_classes'])  # ['btn-primary', 'p-3'] - conflicting classes
print(result['keep_classes'])    # ['btn'] - non-conflicting classes  
print(result['add_classes'])     # ['csso-my_button'] - generated override class
print(result['generated_css'])   # Complete CSS with !important only where needed
print(result['fallback_inline']) # {'background': '#28a745', 'padding': '2rem'}
```

### Multi-Rule Support

Process multiple CSS rules including pseudo-selectors:

```python
# Process only first CSS rule/class (default)
merger = CSSMerger(rule_selection='first')
result = merger.merge(
    ".btn { color: red; } .btn:hover { color: darkred; }",
    {"background": "blue"}
)
# Only .btn is processed, warning about ignored rules

# Process ALL rules including pseudo-selectors
merger = CSSMerger(rule_selection='all')
result = merger.merge(
    ".btn { color: red; } .btn:hover { color: darkred; }",
    {"background": "blue"}
)
# Both .btn and .btn:hover get background: blue
```

## Combinator System (v0.3.0)

The Combinator system automatically detects conflicts between existing CSS classes and your desired style overrides, intelligently managing which classes to keep, remove, or add.

### When to Use Combinator vs CSSMerger

**Use Combinator when:**
- Working with existing CSS frameworks (Bootstrap, Tailwind, etc.)
- You need automatic conflict detection
- Managing class lists on existing elements
- You want to minimize !important usage

**Use CSSMerger when:**
- Generating CSS from scratch
- You need fine-grained control over merge strategies
- Building CSS programmatically
- Working with CSS strings rather than class lists

### Basic Combinator Usage

```python
from csscade.combinator import Combinator

# Step 1: Load your CSS
combinator = Combinator()
combinator.load_css(['bootstrap.css', 'theme.css'])  # Can be files or CSS strings

# Step 2: Process an element
result = combinator.process(
    element_classes=['col-lg-6', 'bg-primary', 'p-3', 'text-white'],
    overrides={
        'background': 'linear-gradient(to right, #667eea, #764ba2)',
        'padding': '2rem',
        'margin': '1rem'
    },
    element_id='hero_section'
)

# Step 3: Use the results
print(result['remove_classes'])  # ['bg-primary', 'p-3'] - have conflicts
print(result['keep_classes'])    # ['col-lg-6', 'text-white'] - no conflicts
print(result['add_classes'])     # ['csso-hero_section'] - new override class
```

### Combinator with Pseudo-selectors and Media Queries

```python
result = combinator.process(
    element_classes=['btn', 'btn-primary'],
    overrides={
        'background': '#28a745',
        'padding': '1rem 2rem',
        ':hover': {
            'background': '#218838',
            'transform': 'scale(1.05)'
        },
        ':focus': {
            'outline': '3px solid #ffc107'
        },
        '@media (min-width: 768px)': {
            'padding': '1.5rem 3rem',
            'font-size': '1.25rem'
        }
    },
    element_id='cta_button'
)

# Generated CSS includes:
# - Base styles with !important only for conflicting properties
# - Hover and focus states
# - Media query for responsive design
print(result['generated_css'])
```

### Processing HTML Elements Directly

```python
# You can also process HTML strings directly
html = '<div class="card shadow p-4 mt-3">Content</div>'

result = combinator.process_element(
    html=html,
    overrides={
        'background': '#f8f9fa',
        'padding': '2rem',
        'margin-top': '2rem'
    },
    element_id='custom_card'
)
```

### Batch Processing Multiple Elements

```python
# Process multiple elements efficiently
elements = [
    {
        'element_classes': ['btn', 'btn-primary'],
        'overrides': {'background': '#28a745'},
        'element_id': 'btn1'
    },
    {
        'element_classes': ['card', 'p-3'],
        'overrides': {'padding': '2rem'},
        'element_id': 'card1'
    }
]

results = combinator.process_batch(elements)
```

### Understanding Conflict Detection

The Combinator uses intelligent conflict detection that understands CSS property relationships:

```python
# Example: Shorthand vs Longhand conflicts
result = combinator.process(
    element_classes=['p-3', 'pt-2', 'border', 'rounded'],
    overrides={
        'padding-top': '3rem',    # Conflicts with both p-3 and pt-2
        'border-color': '#ff0000'  # Conflicts with border shorthand
    },
    element_id='test'
)

# Result:
# - Removes: ['p-3', 'pt-2', 'border'] (all have conflicts)
# - Keeps: ['rounded'] (border-radius is independent)
```

### Complete Combinator Example

```python
from csscade.combinator import Combinator

# Load CSS from your framework
combinator = Combinator()
combinator.load_css([
    'path/to/bootstrap.min.css',
    'path/to/custom-theme.css'
])

# Define your element and overrides
element_classes = [
    'container-fluid', 'row', 'col-lg-6',
    'bg-gradient-primary', 'shadow-lg',
    'text-white', 'p-5', 'rounded-lg'
]

style_overrides = {
    # Regular properties
    'background': 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
    'padding': '3rem',
    'box-shadow': '0 20px 40px rgba(0,0,0,0.1)',
    
    # Pseudo-selectors
    ':hover': {
        'transform': 'translateY(-5px)',
        'box-shadow': '0 25px 50px rgba(0,0,0,0.15)'
    },
    
    # Media queries
    '@media (min-width: 992px)': {
        'padding': '5rem',
        'font-size': '1.25rem'
    }
}

# Process the element
result = combinator.process(
    element_classes=element_classes,
    overrides=style_overrides,
    element_id='hero_block'
)

# Apply the results to your HTML
print(f"Remove these classes: {result['remove_classes']}")
print(f"Keep these classes: {result['keep_classes']}")
print(f"Add this class: {result['add_classes'][0]}")

# Inject the generated CSS
print(f"<style>\n{result['generated_css']}\n</style>")

# Or use inline styles as fallback (excludes pseudo-selectors and media queries)
print(f"Inline fallback: {result['fallback_inline']}")
```

### Combinator API Reference

#### Constructor
```python
combinator = Combinator()
```

#### Methods

**`load_css(css_sources)`**
- Load CSS files or strings for analysis
- `css_sources`: List of file paths or CSS strings

**`process(element_classes, overrides, element_id)`**
- Process style overrides for given classes
- `element_classes`: List of CSS class names
- `overrides`: Dictionary of CSS properties (supports pseudo-selectors and media queries)
- `element_id`: Unique identifier for generating class names
- Returns: Dictionary with conflict analysis and generated CSS

**`process_element(html, overrides, element_id)`**
- Process an HTML element string
- Automatically extracts classes from HTML

**`process_batch(elements)`**
- Process multiple elements efficiently
- `elements`: List of dictionaries with element configurations

**`clear_cache()`**
- Clear loaded CSS cache

### Result Dictionary Structure

```python
{
    'remove_classes': [],     # Classes that conflict with overrides
    'keep_classes': [],       # Classes without conflicts
    'add_classes': [],        # Generated override class name(s)
    'generated_css': '',      # Complete CSS with override rules
    'fallback_inline': {},    # Inline styles (camelCase, no pseudo/media)
    'conflicts_found': []     # Detailed conflict descriptions
}
```

## CSSMerger System

### CSSMerger Merge Modes

#### 1. Permanent Mode
**Directly modifies the original CSS rule. Best for build-time CSS generation.**

```python
merger = CSSMerger(mode='permanent')  # Required parameter

result = merger.merge(
    ".card { color: red; padding: 10px; }",
    {"color": "blue", "margin": "20px"}
)

# Output:
print(result['css'][0])
# .card {
#   color: blue;      /* Changed */
#   padding: 10px;    /* Preserved */
#   margin: 20px;     /* Added */
# }

# Usage: Apply the modified CSS directly
# No class changes needed
```

#### 2. Component Mode (Default)
**Creates an override class while preserving the original. Perfect for theming systems.**

```python
merger = CSSMerger(mode='component')  # Optional (this is default)

result = merger.merge(
    ".btn { color: red; padding: 10px; }",
    {"color": "blue", "margin": "5px"}
)

# Output:
print(result['css'][0])
# .csscade-btn-3f4a {
#   color: blue;
#   padding: 10px;
#   margin: 5px;
# }

print(result['add'])       # ['csscade-btn-3f4a']
print(result['preserve'])  # ['btn']

# Usage: element.className = "btn csscade-btn-3f4a"
```

#### 3. Replace Mode
**Creates a complete replacement class. Best for total style replacement.**

```python
merger = CSSMerger(mode='replace')  # Required parameter

result = merger.merge(
    ".old-style { color: red; padding: 10px; }",
    {"color": "blue", "margin": "5px"}
)

# Output:
print(result['css'][0])
# .csscade-8d2f {
#   color: blue;
#   padding: 10px;
#   margin: 5px;
# }

print(result['add'])     # ['csscade-8d2f']
print(result['remove'])  # ['old-style']

# Usage: element.className = "csscade-8d2f"
```

## CSSMerger Apply To Parameter (Selective Override)

The `apply_to` parameter lets you target specific rules when using `rule_selection='all'`.

### Basic Example

```python
merger = CSSMerger(rule_selection='all')

source = """
.btn { background: blue; color: white; }
.btn:hover { background: darkblue; }
"""

# Apply to all rules (default)
result = merger.merge(source, {"border": "2px solid red"}, apply_to='all')
# Both .btn and .btn:hover get the border

# Apply to base rule only
result = merger.merge(source, {"border": "2px solid red"}, apply_to='base')
# Only .btn gets the border, .btn:hover remains unchanged

# Apply to specific pseudo-selector
result = merger.merge(source, {"border": "2px solid red"}, apply_to=[':hover'])
# Only .btn:hover gets the border
```

### Advanced Example with Multiple Targets

```python
source = """
.btn { background: blue; }
.btn:hover { background: darkblue; }
.btn:active { background: navy; }
.btn:focus { outline: none; }
"""

# Target multiple specific states
result = merger.merge(
    source,
    {"box-shadow": "0 2px 4px rgba(0,0,0,0.2)"},
    apply_to=[':hover', ':focus']
)
# Only :hover and :focus states get the box-shadow
```

### Available Apply To Options

**Special Keywords:**
- `'all'` - Apply to all rules (default)
- `'base'` - Apply to base rule only (no pseudo-selectors)
- `'states'` - Apply only to pseudo-class selectors (all states)

**Wildcards:**
- `'*'` - Apply to all rules (same as 'all')
- `'.*'` - Apply to all base classes (no pseudo-selectors)
- `'*:hover'` - Apply to all rules with :hover pseudo-class
- `'*:active'` - Apply to all rules with :active pseudo-class
- `'*:focus'` - Apply to all rules with :focus pseudo-class

**Specific Selectors:**
- `['.btn']` - Apply to specific class (base rule only)
- `['.btn:hover']` - Apply to specific class with pseudo-selector
- `[':hover']` - Apply to any rule with :hover pseudo-class
- `[':active']` - Apply to any rule with :active pseudo-class
- `[':focus']` - Apply to any rule with :focus pseudo-class

**Multiple Targets:**
- `[':hover', ':active']` - Apply to multiple pseudo-selectors
- `['.btn', '.btn:hover']` - Apply to specific class and its hover state
- `['base', ':active']` - Apply to base rules and :active states

## CSSMerger Conflict Resolution

### !important Handling

CSSCade provides 5 strategies for handling !important declarations:

```python
# 'match' strategy (default) - Add !important if original had it
merger = CSSMerger(conflict_resolution={'important': 'match'})
result = merger.merge(
    ".text { color: blue !important; }",
    {"color": "red"}
)
# Output: .text { color: red !important; }

# 'respect' strategy - Never override !important
merger = CSSMerger(conflict_resolution={'important': 'respect'})
result = merger.merge(
    ".text { color: blue !important; }",
    {"color": "red"}
)
# Output: .text { color: blue !important; }  # Original preserved

# Other strategies:
# 'override': Override but don't add !important
# 'force': Always add !important to overrides
# 'strip': Remove all !important declarations
```

### Shorthand Properties

CSSCade offers three strategies for handling shorthand properties:

#### 1. Cascade Strategy (Default)
**Simple CSS cascade - later properties override**

```python
merger = CSSMerger(shorthand_strategy='cascade')
result = merger.merge(
    ".box { margin: 10px; padding: 20px; }",
    {"margin-top": "30px", "padding": "15px"}
)
# Output: .box {
#   margin: 10px;
#   padding: 15px;      /* Fully replaced */
#   margin-top: 30px;   /* Cascades over margin */
# }
```

#### 2. Smart Strategy
**Intelligent merging for margin/padding, cascade for complex properties**

```python
merger = CSSMerger(shorthand_strategy='smart')
result = merger.merge(
    ".box { margin: 10px; padding: 20px; }",
    {"margin-top": "30px", "padding": "15px"}
)
# Output: .box {
#   margin: 30px 10px 10px;  /* Smart merge: top changed, others preserved */
#   padding: 15px;           /* Fully replaced */
# }
```

#### 3. Expand Strategy
**Full expansion of all shorthands**

```python
merger = CSSMerger(shorthand_strategy='expand')
result = merger.merge(
    ".box { border: 1px solid red; }",
    {"border-width": "3px"}
)
# Output: .box {
#   border-top-width: 3px;
#   border-right-width: 3px;
#   border-bottom-width: 3px;
#   border-left-width: 3px;
#   border-top-style: solid;
#   border-right-style: solid;
#   /* ... all properties expanded ... */
# }
```

## CSSMerger Naming Configuration

Control how override classes are generated:

```python
# Semantic naming (default) - Readable class names
merger = CSSMerger(naming={
    'strategy': 'semantic',  # my-btn-3f4a
    'prefix': 'my-',
    'suffix': '-override'
})
# Output: my-btn-3f4a-override

# Hash naming - Content-based unique identifiers
merger = CSSMerger(naming={
    'strategy': 'hash',      # css-7a9f2c
    'hash_length': 6
})
# Output: css-7a9f2c (same content = same hash)

# Sequential naming - Simple counters
merger = CSSMerger(naming={
    'strategy': 'sequential'  # style-1, style-2, style-3
})
# Output: style-1
```

**Default naming configuration:**
```python
{
    'strategy': 'semantic',    # Readable names
    'prefix': 'csscade-',      # Default prefix
    'suffix': '',              # No suffix by default
    'hash_length': 8           # For hash strategy
}
```

## CSSMerger Validation Configuration

Catch CSS errors and typos with optional validation:

```python
# Development - Helpful warnings
merger = CSSMerger(validation={
    'enabled': True,
    'check_values': True  # Validate color values, units, etc.
})

result = merger.merge(
    ".card { padding: 10px; }",
    {
        "fake-property": "value",     # Unknown property
        "color": "not-a-color",       # Invalid value
        "margin": "10px",
        "margin-top": "20px"          # Duplicate warning
    }
)
# Warnings: [
#   "Unknown CSS property: 'fake-property'",
#   "Invalid color value: 'not-a-color'",
#   "Potential duplicate: 'margin-top'"
# ]

# Production - Strict validation (throws errors)
merger = CSSMerger(validation={
    'enabled': True,
    'strict': True  # Raises exception on invalid CSS
})

# Minimal - Just enable validation
merger = CSSMerger(validation={'enabled': True})
```

**Default validation configuration:**
```python
{
    'enabled': False,         # Off by default (backwards compatible)
    'strict': False,          # Warnings, not errors
    'check_properties': True, # Check property names when enabled
    'check_values': False,    # Don't check values by default (expensive)
    'allow_vendor': True,     # Allow -webkit-, -moz-, etc.
    'allow_custom': True,     # Allow --css-variables
    'check_duplicates': True  # Warn about duplicate properties
}
```

## CSSMerger Complete Configuration Examples 🎯

### Production Configuration

```python
merger = CSSMerger(
    mode='component',
    rule_selection='all',
    naming={
        'strategy': 'semantic',
        'prefix': 'app-',
        'suffix': ''
    },
    conflict_resolution={
        'important': 'match'
    },
    shorthand_strategy='smart',
    validation={
        'enabled': True,
        'strict': False,
        'check_properties': True
    }
)
```

### Development Configuration

```python
merger = CSSMerger(
    mode='component',
    rule_selection='all',
    naming={'strategy': 'sequential', 'prefix': 'dev-'},
    validation={
        'enabled': True,
        'check_values': True,
        'strict': False
    },
    shorthand_strategy='expand',  # See all properties
    debug=True
)
```

## Advanced CSSMerger Usage 🔧

### Real-World Example: Bootstrap Customization

Customize Bootstrap components while preserving all states:

```python
merger = CSSMerger(
    mode='component',
    rule_selection='all',
    shorthand_strategy='smart'
)

bootstrap_button = """
.btn-primary {
    background: #007bff;
    color: white;
    padding: 0.375rem 0.75rem;
    border: 1px solid #007bff;
}
.btn-primary:hover {
    background: #0056b3;
    border-color: #004085;
}
.btn-primary:active {
    background: #004085;
}
"""

brand_overrides = {
    "background": "#28a745",     # Green instead of blue
    "border-color": "#28a745",
    "font-weight": "bold"
}

result = merger.merge(bootstrap_button, brand_overrides, apply_to='all')

# Output:
# .app-btn-primary-x1a3 { 
#     background: #28a745; 
#     color: white;
#     padding: 0.375rem 0.75rem;
#     border-color: #28a745;
#     font-weight: bold;
# }
# .app-btn-primary-x1a3:hover {
#     background: #28a745;
#     border-color: #28a745;
#     font-weight: bold;
# }
# .app-btn-primary-x1a3:active {
#     background: #28a745;
#     border-color: #28a745;
#     font-weight: bold;
# }

# Usage: <button class="btn-primary app-btn-primary-x1a3">
```

### Runtime CSS Manipulation (Inline Styling)

Generate inline styles for dynamic theming:

```python
merger = CSSMerger(mode='permanent')

# User's theme preferences
user_theme = {
    "primary-color": "#FF5722",
    "font-size": "18px"
}

# Generate inline styles
result = merger.merge(
    "body { color: #333; font-size: 16px; }",
    user_theme
)

# Apply dynamically
element.style.cssText = result['css'][0]
```

### Batch Operations

Process multiple CSS operations efficiently:

```python
merger = CSSMerger(mode='component')
batch = merger.batch()

# Queue multiple operations
batch.add(".header { color: black; }", {"background": "white"})
batch.add(".footer { padding: 20px; }", {"border-top": "1px solid gray"})
batch.add(".sidebar { width: 200px; }", {"background": "#f5f5f5"})

# Execute all at once
results = batch.execute()

for i, result in enumerate(results):
    print(f"Operation {i+1}: {result['add']}")
```

## CSSMerger Result Dictionary Structure

All merge operations return a consistent structure:

```python
{
    'css': [],        # List of generated CSS strings (always list)
    'add': [],        # Classes to add to element (always list)
    'remove': [],     # Classes to remove from element (always list)
    'preserve': [],   # Original classes to keep (always list)
    'warnings': [],   # Validation/conflict warnings (always list)
    'info': []        # Informational messages (always list)
}
```

## CSSMerger API Reference

### CSSMerger Constructor

```python
CSSMerger(
    mode='component',              # 'permanent'|'component'|'replace'
    rule_selection='first',        # 'first'|'all'
    naming={                       # Class naming configuration
        'strategy': 'semantic',    # 'semantic'|'hash'|'sequential'
        'prefix': 'csscade-',
        'suffix': '',
        'hash_length': 8
    },
    conflict_resolution={          # Conflict handling
        'important': 'match'       # 'match'|'respect'|'override'|'force'|'strip'
    },
    shorthand_strategy='cascade',  # 'cascade'|'smart'|'expand'
    validation={                   # CSS validation
        'enabled': False,
        'strict': False,
        'check_properties': True,
        'check_values': False,
        'allow_vendor': True,
        'allow_custom': True,
        'check_duplicates': True
    },
    debug=False                    # Enable debug output
)
```

### merge() Method

```python
result = merger.merge(
    source,          # CSS string, rule, or properties dict
    override,        # Properties dict or CSS string
    component_id=None,  # Optional unique identifier
    apply_to='all'   # Which rules to apply overrides to
)
```

## Default Values Reference

| Parameter | Default Value | Description |
|-----------|--------------|-------------|
| `mode` | `'component'` | Merge strategy |
| `rule_selection` | `'first'` | Process first CSS rule/class only |
| `shorthand_strategy` | `'cascade'` | Simple CSS cascade |
| `naming.strategy` | `'semantic'` | Readable class names |
| `naming.prefix` | `'csscade-'` | Class name prefix |
| `validation.enabled` | `False` | Validation off by default |
| `conflict_resolution.important` | `'match'` | Match original !important |

## Testing

Run the comprehensive test suite:

```bash
# Basic test
python _test_comprehensive.py

# Run all tests
python -m pytest tests/
```

## Contributing

Contributions are welcome! Please check out our [Contributing Guide](CONTRIBUTING.md) for details.

## License

MIT License - see [LICENSE](LICENSE) file for details.

## Links

- [PyPI Package](https://pypi.org/project/csscade/)
- [GitHub Repository](https://github.com/yourusername/csscade)
- [Issue Tracker](https://github.com/yourusername/csscade/issues)

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "csscade",
    "maintainer": "sebieire",
    "docs_url": null,
    "requires_python": ">=3.7",
    "maintainer_email": null,
    "keywords": "css, merge, conflict-resolution, styling, theme, customization",
    "author": "sebieire",
    "author_email": null,
    "download_url": "https://files.pythonhosted.org/packages/42/01/0e283c3904b0fa2849e5b182cc3c5c14cd3734e03db88b8ae468471a0ea9/csscade-0.3.0.tar.gz",
    "platform": null,
    "description": "# CSSCade \ud83c\udfa8\n\n[![PyPI version](https://img.shields.io/pypi/v/csscade.svg)](https://pypi.org/project/csscade/)\n[![Python Support](https://img.shields.io/pypi/pyversions/csscade.svg)](https://pypi.org/project/csscade/)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\n**Intelligent CSS merging and conflict resolution with two powerful systems**\n\nCSSCade provides two complementary systems for CSS manipulation:\n\n1. **CSSMerger** - Property-level CSS merging with multiple strategies\n2. **Combinator** (v0.3.0) - Intelligent conflict detection for existing CSS frameworks\n\nPerfect for theme customization, CSS framework overrides, runtime CSS manipulation, and CSS-in-JS implementations.\n\nI was looking for something like this in the python ecosystem and couldn't find anything that matched my usecase so here it is. Enjoy!\n\n## Features \u2728\n\n### Core Features\n- **Intelligent Conflict Resolution**: Handle !important, shorthand properties, and duplicates\n- **Pseudo-selector Support**: Full support for :hover, :focus, :before, :after, etc.\n- **Media Query Support**: Responsive design support with @media rules\n- **Production Ready**: Used in real-world applications for dynamic theming\n\n### CSSMerger System\n- **3 Merge Modes**: Permanent, Component, and Replace strategies\n- **Multi-Rule Support**: Process all CSS rules including pseudo-selectors with `rule_selection='all'`\n- **Selective Application**: Target specific rules with the `apply_to` parameter\n- **Smart Property Merging**: Configurable shorthand strategies (cascade, smart, expand)\n- **CSS Validation**: Optional property and value validation with helpful warnings\n- **Flexible Naming**: Multiple class naming strategies (semantic, hash, sequential)\n\n### Combinator System (v0.3.0)\n- **Automatic Conflict Detection**: Analyzes existing CSS classes and finds conflicts\n- **Framework Integration**: Works with Bootstrap, Tailwind, or any CSS framework\n- **Smart Class Management**: Automatically determines which classes to keep/remove\n- **Override Generation**: Creates optimized override CSS with minimal !important usage\n- **Inline Fallback**: Generates React/JS-compatible inline styles\n- **Batch Processing**: Efficiently process multiple elements\n\n## Installation\n\n```bash\npip install csscade\n```\n\n**Minimal Dependencies:** CSSCade has only ONE runtime dependency (`cssutils`) making it lightweight and fast to install!\n\n## Quick Start \ud83d\ude80\n\nCSSCade offers two approaches depending on your needs:\n\n### Method 1: CSSMerger - For Manual CSS Generation\n\n```python\nfrom csscade import CSSMerger\n\n# Create a merger (defaults to 'component' mode)\nmerger = CSSMerger()\n\n# Merge CSS\nsource_css = \".btn { color: red; padding: 10px; }\"\noverrides = {\"color\": \"blue\", \"margin\": \"5px\"}\n\nresult = merger.merge(source_css, overrides)\n\n# Result structure:\nprint(result['css'])        # ['.csscade-btn-3f4a { color: blue; padding: 10px; margin: 5px; }']\nprint(result['add'])        # ['csscade-btn-3f4a']\nprint(result['preserve'])   # ['btn']\n```\n\n### Method 2: Combinator - For Working with Existing CSS Frameworks\n\n```python\nfrom csscade.combinator import Combinator\n\n# Load your CSS framework\ncombinator = Combinator()\ncombinator.load_css(['bootstrap.css', 'custom.css'])\n\n# Process an element with conflicts\nresult = combinator.process(\n    element_classes=['btn', 'btn-primary', 'p-3'],\n    overrides={\n        'background': '#28a745',\n        'padding': '2rem',\n        ':hover': {\n            'background': '#218838'\n        }\n    },\n    element_id='my_button'\n)\n\n# Result structure:\nprint(result['remove_classes'])  # ['btn-primary', 'p-3'] - conflicting classes\nprint(result['keep_classes'])    # ['btn'] - non-conflicting classes  \nprint(result['add_classes'])     # ['csso-my_button'] - generated override class\nprint(result['generated_css'])   # Complete CSS with !important only where needed\nprint(result['fallback_inline']) # {'background': '#28a745', 'padding': '2rem'}\n```\n\n### Multi-Rule Support\n\nProcess multiple CSS rules including pseudo-selectors:\n\n```python\n# Process only first CSS rule/class (default)\nmerger = CSSMerger(rule_selection='first')\nresult = merger.merge(\n    \".btn { color: red; } .btn:hover { color: darkred; }\",\n    {\"background\": \"blue\"}\n)\n# Only .btn is processed, warning about ignored rules\n\n# Process ALL rules including pseudo-selectors\nmerger = CSSMerger(rule_selection='all')\nresult = merger.merge(\n    \".btn { color: red; } .btn:hover { color: darkred; }\",\n    {\"background\": \"blue\"}\n)\n# Both .btn and .btn:hover get background: blue\n```\n\n## Combinator System (v0.3.0)\n\nThe Combinator system automatically detects conflicts between existing CSS classes and your desired style overrides, intelligently managing which classes to keep, remove, or add.\n\n### When to Use Combinator vs CSSMerger\n\n**Use Combinator when:**\n- Working with existing CSS frameworks (Bootstrap, Tailwind, etc.)\n- You need automatic conflict detection\n- Managing class lists on existing elements\n- You want to minimize !important usage\n\n**Use CSSMerger when:**\n- Generating CSS from scratch\n- You need fine-grained control over merge strategies\n- Building CSS programmatically\n- Working with CSS strings rather than class lists\n\n### Basic Combinator Usage\n\n```python\nfrom csscade.combinator import Combinator\n\n# Step 1: Load your CSS\ncombinator = Combinator()\ncombinator.load_css(['bootstrap.css', 'theme.css'])  # Can be files or CSS strings\n\n# Step 2: Process an element\nresult = combinator.process(\n    element_classes=['col-lg-6', 'bg-primary', 'p-3', 'text-white'],\n    overrides={\n        'background': 'linear-gradient(to right, #667eea, #764ba2)',\n        'padding': '2rem',\n        'margin': '1rem'\n    },\n    element_id='hero_section'\n)\n\n# Step 3: Use the results\nprint(result['remove_classes'])  # ['bg-primary', 'p-3'] - have conflicts\nprint(result['keep_classes'])    # ['col-lg-6', 'text-white'] - no conflicts\nprint(result['add_classes'])     # ['csso-hero_section'] - new override class\n```\n\n### Combinator with Pseudo-selectors and Media Queries\n\n```python\nresult = combinator.process(\n    element_classes=['btn', 'btn-primary'],\n    overrides={\n        'background': '#28a745',\n        'padding': '1rem 2rem',\n        ':hover': {\n            'background': '#218838',\n            'transform': 'scale(1.05)'\n        },\n        ':focus': {\n            'outline': '3px solid #ffc107'\n        },\n        '@media (min-width: 768px)': {\n            'padding': '1.5rem 3rem',\n            'font-size': '1.25rem'\n        }\n    },\n    element_id='cta_button'\n)\n\n# Generated CSS includes:\n# - Base styles with !important only for conflicting properties\n# - Hover and focus states\n# - Media query for responsive design\nprint(result['generated_css'])\n```\n\n### Processing HTML Elements Directly\n\n```python\n# You can also process HTML strings directly\nhtml = '<div class=\"card shadow p-4 mt-3\">Content</div>'\n\nresult = combinator.process_element(\n    html=html,\n    overrides={\n        'background': '#f8f9fa',\n        'padding': '2rem',\n        'margin-top': '2rem'\n    },\n    element_id='custom_card'\n)\n```\n\n### Batch Processing Multiple Elements\n\n```python\n# Process multiple elements efficiently\nelements = [\n    {\n        'element_classes': ['btn', 'btn-primary'],\n        'overrides': {'background': '#28a745'},\n        'element_id': 'btn1'\n    },\n    {\n        'element_classes': ['card', 'p-3'],\n        'overrides': {'padding': '2rem'},\n        'element_id': 'card1'\n    }\n]\n\nresults = combinator.process_batch(elements)\n```\n\n### Understanding Conflict Detection\n\nThe Combinator uses intelligent conflict detection that understands CSS property relationships:\n\n```python\n# Example: Shorthand vs Longhand conflicts\nresult = combinator.process(\n    element_classes=['p-3', 'pt-2', 'border', 'rounded'],\n    overrides={\n        'padding-top': '3rem',    # Conflicts with both p-3 and pt-2\n        'border-color': '#ff0000'  # Conflicts with border shorthand\n    },\n    element_id='test'\n)\n\n# Result:\n# - Removes: ['p-3', 'pt-2', 'border'] (all have conflicts)\n# - Keeps: ['rounded'] (border-radius is independent)\n```\n\n### Complete Combinator Example\n\n```python\nfrom csscade.combinator import Combinator\n\n# Load CSS from your framework\ncombinator = Combinator()\ncombinator.load_css([\n    'path/to/bootstrap.min.css',\n    'path/to/custom-theme.css'\n])\n\n# Define your element and overrides\nelement_classes = [\n    'container-fluid', 'row', 'col-lg-6',\n    'bg-gradient-primary', 'shadow-lg',\n    'text-white', 'p-5', 'rounded-lg'\n]\n\nstyle_overrides = {\n    # Regular properties\n    'background': 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',\n    'padding': '3rem',\n    'box-shadow': '0 20px 40px rgba(0,0,0,0.1)',\n    \n    # Pseudo-selectors\n    ':hover': {\n        'transform': 'translateY(-5px)',\n        'box-shadow': '0 25px 50px rgba(0,0,0,0.15)'\n    },\n    \n    # Media queries\n    '@media (min-width: 992px)': {\n        'padding': '5rem',\n        'font-size': '1.25rem'\n    }\n}\n\n# Process the element\nresult = combinator.process(\n    element_classes=element_classes,\n    overrides=style_overrides,\n    element_id='hero_block'\n)\n\n# Apply the results to your HTML\nprint(f\"Remove these classes: {result['remove_classes']}\")\nprint(f\"Keep these classes: {result['keep_classes']}\")\nprint(f\"Add this class: {result['add_classes'][0]}\")\n\n# Inject the generated CSS\nprint(f\"<style>\\n{result['generated_css']}\\n</style>\")\n\n# Or use inline styles as fallback (excludes pseudo-selectors and media queries)\nprint(f\"Inline fallback: {result['fallback_inline']}\")\n```\n\n### Combinator API Reference\n\n#### Constructor\n```python\ncombinator = Combinator()\n```\n\n#### Methods\n\n**`load_css(css_sources)`**\n- Load CSS files or strings for analysis\n- `css_sources`: List of file paths or CSS strings\n\n**`process(element_classes, overrides, element_id)`**\n- Process style overrides for given classes\n- `element_classes`: List of CSS class names\n- `overrides`: Dictionary of CSS properties (supports pseudo-selectors and media queries)\n- `element_id`: Unique identifier for generating class names\n- Returns: Dictionary with conflict analysis and generated CSS\n\n**`process_element(html, overrides, element_id)`**\n- Process an HTML element string\n- Automatically extracts classes from HTML\n\n**`process_batch(elements)`**\n- Process multiple elements efficiently\n- `elements`: List of dictionaries with element configurations\n\n**`clear_cache()`**\n- Clear loaded CSS cache\n\n### Result Dictionary Structure\n\n```python\n{\n    'remove_classes': [],     # Classes that conflict with overrides\n    'keep_classes': [],       # Classes without conflicts\n    'add_classes': [],        # Generated override class name(s)\n    'generated_css': '',      # Complete CSS with override rules\n    'fallback_inline': {},    # Inline styles (camelCase, no pseudo/media)\n    'conflicts_found': []     # Detailed conflict descriptions\n}\n```\n\n## CSSMerger System\n\n### CSSMerger Merge Modes\n\n#### 1. Permanent Mode\n**Directly modifies the original CSS rule. Best for build-time CSS generation.**\n\n```python\nmerger = CSSMerger(mode='permanent')  # Required parameter\n\nresult = merger.merge(\n    \".card { color: red; padding: 10px; }\",\n    {\"color\": \"blue\", \"margin\": \"20px\"}\n)\n\n# Output:\nprint(result['css'][0])\n# .card {\n#   color: blue;      /* Changed */\n#   padding: 10px;    /* Preserved */\n#   margin: 20px;     /* Added */\n# }\n\n# Usage: Apply the modified CSS directly\n# No class changes needed\n```\n\n#### 2. Component Mode (Default)\n**Creates an override class while preserving the original. Perfect for theming systems.**\n\n```python\nmerger = CSSMerger(mode='component')  # Optional (this is default)\n\nresult = merger.merge(\n    \".btn { color: red; padding: 10px; }\",\n    {\"color\": \"blue\", \"margin\": \"5px\"}\n)\n\n# Output:\nprint(result['css'][0])\n# .csscade-btn-3f4a {\n#   color: blue;\n#   padding: 10px;\n#   margin: 5px;\n# }\n\nprint(result['add'])       # ['csscade-btn-3f4a']\nprint(result['preserve'])  # ['btn']\n\n# Usage: element.className = \"btn csscade-btn-3f4a\"\n```\n\n#### 3. Replace Mode\n**Creates a complete replacement class. Best for total style replacement.**\n\n```python\nmerger = CSSMerger(mode='replace')  # Required parameter\n\nresult = merger.merge(\n    \".old-style { color: red; padding: 10px; }\",\n    {\"color\": \"blue\", \"margin\": \"5px\"}\n)\n\n# Output:\nprint(result['css'][0])\n# .csscade-8d2f {\n#   color: blue;\n#   padding: 10px;\n#   margin: 5px;\n# }\n\nprint(result['add'])     # ['csscade-8d2f']\nprint(result['remove'])  # ['old-style']\n\n# Usage: element.className = \"csscade-8d2f\"\n```\n\n## CSSMerger Apply To Parameter (Selective Override)\n\nThe `apply_to` parameter lets you target specific rules when using `rule_selection='all'`.\n\n### Basic Example\n\n```python\nmerger = CSSMerger(rule_selection='all')\n\nsource = \"\"\"\n.btn { background: blue; color: white; }\n.btn:hover { background: darkblue; }\n\"\"\"\n\n# Apply to all rules (default)\nresult = merger.merge(source, {\"border\": \"2px solid red\"}, apply_to='all')\n# Both .btn and .btn:hover get the border\n\n# Apply to base rule only\nresult = merger.merge(source, {\"border\": \"2px solid red\"}, apply_to='base')\n# Only .btn gets the border, .btn:hover remains unchanged\n\n# Apply to specific pseudo-selector\nresult = merger.merge(source, {\"border\": \"2px solid red\"}, apply_to=[':hover'])\n# Only .btn:hover gets the border\n```\n\n### Advanced Example with Multiple Targets\n\n```python\nsource = \"\"\"\n.btn { background: blue; }\n.btn:hover { background: darkblue; }\n.btn:active { background: navy; }\n.btn:focus { outline: none; }\n\"\"\"\n\n# Target multiple specific states\nresult = merger.merge(\n    source,\n    {\"box-shadow\": \"0 2px 4px rgba(0,0,0,0.2)\"},\n    apply_to=[':hover', ':focus']\n)\n# Only :hover and :focus states get the box-shadow\n```\n\n### Available Apply To Options\n\n**Special Keywords:**\n- `'all'` - Apply to all rules (default)\n- `'base'` - Apply to base rule only (no pseudo-selectors)\n- `'states'` - Apply only to pseudo-class selectors (all states)\n\n**Wildcards:**\n- `'*'` - Apply to all rules (same as 'all')\n- `'.*'` - Apply to all base classes (no pseudo-selectors)\n- `'*:hover'` - Apply to all rules with :hover pseudo-class\n- `'*:active'` - Apply to all rules with :active pseudo-class\n- `'*:focus'` - Apply to all rules with :focus pseudo-class\n\n**Specific Selectors:**\n- `['.btn']` - Apply to specific class (base rule only)\n- `['.btn:hover']` - Apply to specific class with pseudo-selector\n- `[':hover']` - Apply to any rule with :hover pseudo-class\n- `[':active']` - Apply to any rule with :active pseudo-class\n- `[':focus']` - Apply to any rule with :focus pseudo-class\n\n**Multiple Targets:**\n- `[':hover', ':active']` - Apply to multiple pseudo-selectors\n- `['.btn', '.btn:hover']` - Apply to specific class and its hover state\n- `['base', ':active']` - Apply to base rules and :active states\n\n## CSSMerger Conflict Resolution\n\n### !important Handling\n\nCSSCade provides 5 strategies for handling !important declarations:\n\n```python\n# 'match' strategy (default) - Add !important if original had it\nmerger = CSSMerger(conflict_resolution={'important': 'match'})\nresult = merger.merge(\n    \".text { color: blue !important; }\",\n    {\"color\": \"red\"}\n)\n# Output: .text { color: red !important; }\n\n# 'respect' strategy - Never override !important\nmerger = CSSMerger(conflict_resolution={'important': 'respect'})\nresult = merger.merge(\n    \".text { color: blue !important; }\",\n    {\"color\": \"red\"}\n)\n# Output: .text { color: blue !important; }  # Original preserved\n\n# Other strategies:\n# 'override': Override but don't add !important\n# 'force': Always add !important to overrides\n# 'strip': Remove all !important declarations\n```\n\n### Shorthand Properties\n\nCSSCade offers three strategies for handling shorthand properties:\n\n#### 1. Cascade Strategy (Default)\n**Simple CSS cascade - later properties override**\n\n```python\nmerger = CSSMerger(shorthand_strategy='cascade')\nresult = merger.merge(\n    \".box { margin: 10px; padding: 20px; }\",\n    {\"margin-top\": \"30px\", \"padding\": \"15px\"}\n)\n# Output: .box {\n#   margin: 10px;\n#   padding: 15px;      /* Fully replaced */\n#   margin-top: 30px;   /* Cascades over margin */\n# }\n```\n\n#### 2. Smart Strategy\n**Intelligent merging for margin/padding, cascade for complex properties**\n\n```python\nmerger = CSSMerger(shorthand_strategy='smart')\nresult = merger.merge(\n    \".box { margin: 10px; padding: 20px; }\",\n    {\"margin-top\": \"30px\", \"padding\": \"15px\"}\n)\n# Output: .box {\n#   margin: 30px 10px 10px;  /* Smart merge: top changed, others preserved */\n#   padding: 15px;           /* Fully replaced */\n# }\n```\n\n#### 3. Expand Strategy\n**Full expansion of all shorthands**\n\n```python\nmerger = CSSMerger(shorthand_strategy='expand')\nresult = merger.merge(\n    \".box { border: 1px solid red; }\",\n    {\"border-width\": \"3px\"}\n)\n# Output: .box {\n#   border-top-width: 3px;\n#   border-right-width: 3px;\n#   border-bottom-width: 3px;\n#   border-left-width: 3px;\n#   border-top-style: solid;\n#   border-right-style: solid;\n#   /* ... all properties expanded ... */\n# }\n```\n\n## CSSMerger Naming Configuration\n\nControl how override classes are generated:\n\n```python\n# Semantic naming (default) - Readable class names\nmerger = CSSMerger(naming={\n    'strategy': 'semantic',  # my-btn-3f4a\n    'prefix': 'my-',\n    'suffix': '-override'\n})\n# Output: my-btn-3f4a-override\n\n# Hash naming - Content-based unique identifiers\nmerger = CSSMerger(naming={\n    'strategy': 'hash',      # css-7a9f2c\n    'hash_length': 6\n})\n# Output: css-7a9f2c (same content = same hash)\n\n# Sequential naming - Simple counters\nmerger = CSSMerger(naming={\n    'strategy': 'sequential'  # style-1, style-2, style-3\n})\n# Output: style-1\n```\n\n**Default naming configuration:**\n```python\n{\n    'strategy': 'semantic',    # Readable names\n    'prefix': 'csscade-',      # Default prefix\n    'suffix': '',              # No suffix by default\n    'hash_length': 8           # For hash strategy\n}\n```\n\n## CSSMerger Validation Configuration\n\nCatch CSS errors and typos with optional validation:\n\n```python\n# Development - Helpful warnings\nmerger = CSSMerger(validation={\n    'enabled': True,\n    'check_values': True  # Validate color values, units, etc.\n})\n\nresult = merger.merge(\n    \".card { padding: 10px; }\",\n    {\n        \"fake-property\": \"value\",     # Unknown property\n        \"color\": \"not-a-color\",       # Invalid value\n        \"margin\": \"10px\",\n        \"margin-top\": \"20px\"          # Duplicate warning\n    }\n)\n# Warnings: [\n#   \"Unknown CSS property: 'fake-property'\",\n#   \"Invalid color value: 'not-a-color'\",\n#   \"Potential duplicate: 'margin-top'\"\n# ]\n\n# Production - Strict validation (throws errors)\nmerger = CSSMerger(validation={\n    'enabled': True,\n    'strict': True  # Raises exception on invalid CSS\n})\n\n# Minimal - Just enable validation\nmerger = CSSMerger(validation={'enabled': True})\n```\n\n**Default validation configuration:**\n```python\n{\n    'enabled': False,         # Off by default (backwards compatible)\n    'strict': False,          # Warnings, not errors\n    'check_properties': True, # Check property names when enabled\n    'check_values': False,    # Don't check values by default (expensive)\n    'allow_vendor': True,     # Allow -webkit-, -moz-, etc.\n    'allow_custom': True,     # Allow --css-variables\n    'check_duplicates': True  # Warn about duplicate properties\n}\n```\n\n## CSSMerger Complete Configuration Examples \ud83c\udfaf\n\n### Production Configuration\n\n```python\nmerger = CSSMerger(\n    mode='component',\n    rule_selection='all',\n    naming={\n        'strategy': 'semantic',\n        'prefix': 'app-',\n        'suffix': ''\n    },\n    conflict_resolution={\n        'important': 'match'\n    },\n    shorthand_strategy='smart',\n    validation={\n        'enabled': True,\n        'strict': False,\n        'check_properties': True\n    }\n)\n```\n\n### Development Configuration\n\n```python\nmerger = CSSMerger(\n    mode='component',\n    rule_selection='all',\n    naming={'strategy': 'sequential', 'prefix': 'dev-'},\n    validation={\n        'enabled': True,\n        'check_values': True,\n        'strict': False\n    },\n    shorthand_strategy='expand',  # See all properties\n    debug=True\n)\n```\n\n## Advanced CSSMerger Usage \ud83d\udd27\n\n### Real-World Example: Bootstrap Customization\n\nCustomize Bootstrap components while preserving all states:\n\n```python\nmerger = CSSMerger(\n    mode='component',\n    rule_selection='all',\n    shorthand_strategy='smart'\n)\n\nbootstrap_button = \"\"\"\n.btn-primary {\n    background: #007bff;\n    color: white;\n    padding: 0.375rem 0.75rem;\n    border: 1px solid #007bff;\n}\n.btn-primary:hover {\n    background: #0056b3;\n    border-color: #004085;\n}\n.btn-primary:active {\n    background: #004085;\n}\n\"\"\"\n\nbrand_overrides = {\n    \"background\": \"#28a745\",     # Green instead of blue\n    \"border-color\": \"#28a745\",\n    \"font-weight\": \"bold\"\n}\n\nresult = merger.merge(bootstrap_button, brand_overrides, apply_to='all')\n\n# Output:\n# .app-btn-primary-x1a3 { \n#     background: #28a745; \n#     color: white;\n#     padding: 0.375rem 0.75rem;\n#     border-color: #28a745;\n#     font-weight: bold;\n# }\n# .app-btn-primary-x1a3:hover {\n#     background: #28a745;\n#     border-color: #28a745;\n#     font-weight: bold;\n# }\n# .app-btn-primary-x1a3:active {\n#     background: #28a745;\n#     border-color: #28a745;\n#     font-weight: bold;\n# }\n\n# Usage: <button class=\"btn-primary app-btn-primary-x1a3\">\n```\n\n### Runtime CSS Manipulation (Inline Styling)\n\nGenerate inline styles for dynamic theming:\n\n```python\nmerger = CSSMerger(mode='permanent')\n\n# User's theme preferences\nuser_theme = {\n    \"primary-color\": \"#FF5722\",\n    \"font-size\": \"18px\"\n}\n\n# Generate inline styles\nresult = merger.merge(\n    \"body { color: #333; font-size: 16px; }\",\n    user_theme\n)\n\n# Apply dynamically\nelement.style.cssText = result['css'][0]\n```\n\n### Batch Operations\n\nProcess multiple CSS operations efficiently:\n\n```python\nmerger = CSSMerger(mode='component')\nbatch = merger.batch()\n\n# Queue multiple operations\nbatch.add(\".header { color: black; }\", {\"background\": \"white\"})\nbatch.add(\".footer { padding: 20px; }\", {\"border-top\": \"1px solid gray\"})\nbatch.add(\".sidebar { width: 200px; }\", {\"background\": \"#f5f5f5\"})\n\n# Execute all at once\nresults = batch.execute()\n\nfor i, result in enumerate(results):\n    print(f\"Operation {i+1}: {result['add']}\")\n```\n\n## CSSMerger Result Dictionary Structure\n\nAll merge operations return a consistent structure:\n\n```python\n{\n    'css': [],        # List of generated CSS strings (always list)\n    'add': [],        # Classes to add to element (always list)\n    'remove': [],     # Classes to remove from element (always list)\n    'preserve': [],   # Original classes to keep (always list)\n    'warnings': [],   # Validation/conflict warnings (always list)\n    'info': []        # Informational messages (always list)\n}\n```\n\n## CSSMerger API Reference\n\n### CSSMerger Constructor\n\n```python\nCSSMerger(\n    mode='component',              # 'permanent'|'component'|'replace'\n    rule_selection='first',        # 'first'|'all'\n    naming={                       # Class naming configuration\n        'strategy': 'semantic',    # 'semantic'|'hash'|'sequential'\n        'prefix': 'csscade-',\n        'suffix': '',\n        'hash_length': 8\n    },\n    conflict_resolution={          # Conflict handling\n        'important': 'match'       # 'match'|'respect'|'override'|'force'|'strip'\n    },\n    shorthand_strategy='cascade',  # 'cascade'|'smart'|'expand'\n    validation={                   # CSS validation\n        'enabled': False,\n        'strict': False,\n        'check_properties': True,\n        'check_values': False,\n        'allow_vendor': True,\n        'allow_custom': True,\n        'check_duplicates': True\n    },\n    debug=False                    # Enable debug output\n)\n```\n\n### merge() Method\n\n```python\nresult = merger.merge(\n    source,          # CSS string, rule, or properties dict\n    override,        # Properties dict or CSS string\n    component_id=None,  # Optional unique identifier\n    apply_to='all'   # Which rules to apply overrides to\n)\n```\n\n## Default Values Reference\n\n| Parameter | Default Value | Description |\n|-----------|--------------|-------------|\n| `mode` | `'component'` | Merge strategy |\n| `rule_selection` | `'first'` | Process first CSS rule/class only |\n| `shorthand_strategy` | `'cascade'` | Simple CSS cascade |\n| `naming.strategy` | `'semantic'` | Readable class names |\n| `naming.prefix` | `'csscade-'` | Class name prefix |\n| `validation.enabled` | `False` | Validation off by default |\n| `conflict_resolution.important` | `'match'` | Match original !important |\n\n## Testing\n\nRun the comprehensive test suite:\n\n```bash\n# Basic test\npython _test_comprehensive.py\n\n# Run all tests\npython -m pytest tests/\n```\n\n## Contributing\n\nContributions are welcome! Please check out our [Contributing Guide](CONTRIBUTING.md) for details.\n\n## License\n\nMIT License - see [LICENSE](LICENSE) file for details.\n\n## Links\n\n- [PyPI Package](https://pypi.org/project/csscade/)\n- [GitHub Repository](https://github.com/yourusername/csscade)\n- [Issue Tracker](https://github.com/yourusername/csscade/issues)\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Intelligent CSS merging with conflict resolution and multiple strategies",
    "version": "0.3.0",
    "project_urls": {
        "Documentation": "https://github.com/sebieire/csscade#readme",
        "Homepage": "https://github.com/sebieire/csscade",
        "Issues": "https://github.com/sebieire/csscade/issues",
        "Repository": "https://github.com/sebieire/csscade"
    },
    "split_keywords": [
        "css",
        " merge",
        " conflict-resolution",
        " styling",
        " theme",
        " customization"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "05e98350cb8523b95df73bad70b09f61dda1c90156259388521200a4c8873ed8",
                "md5": "4cf5cfa6f82283ed2b0de3218ee0dab0",
                "sha256": "dcb063333e710ddc258dba246bc674744689b0fb9cb0707665df5bc35f293791"
            },
            "downloads": -1,
            "filename": "csscade-0.3.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "4cf5cfa6f82283ed2b0de3218ee0dab0",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.7",
            "size": 119407,
            "upload_time": "2025-09-10T13:53:49",
            "upload_time_iso_8601": "2025-09-10T13:53:49.358614Z",
            "url": "https://files.pythonhosted.org/packages/05/e9/8350cb8523b95df73bad70b09f61dda1c90156259388521200a4c8873ed8/csscade-0.3.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "42010e283c3904b0fa2849e5b182cc3c5c14cd3734e03db88b8ae468471a0ea9",
                "md5": "332e23685b84fa0364b4306dde1fb517",
                "sha256": "e93a41eb8ab888a21cbffbf4fc4d269897184af4c4f2e63f7a5c62d1d67857f7"
            },
            "downloads": -1,
            "filename": "csscade-0.3.0.tar.gz",
            "has_sig": false,
            "md5_digest": "332e23685b84fa0364b4306dde1fb517",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.7",
            "size": 111734,
            "upload_time": "2025-09-10T13:53:51",
            "upload_time_iso_8601": "2025-09-10T13:53:51.003918Z",
            "url": "https://files.pythonhosted.org/packages/42/01/0e283c3904b0fa2849e5b182cc3c5c14cd3734e03db88b8ae468471a0ea9/csscade-0.3.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-09-10 13:53:51",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "sebieire",
    "github_project": "csscade#readme",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "requirements": [
        {
            "name": "cssutils",
            "specs": [
                [
                    ">=",
                    "2.11.1"
                ]
            ]
        }
    ],
    "lcname": "csscade"
}
        
Elapsed time: 4.38555s