CSS Patterns
Best practices for styling WildflowerJS components without build tools or framework overhead.
Component-Scoped CSS with :where()
Since every WildflowerJS component has a data-component attribute, you can use native CSS attribute selectors to scope styles without any framework code:
/* Scoped to kanban-column component */
:where([data-component="kanban-column"]) {
.header {
font-weight: bold;
padding: 1rem;
}
.card {
border-radius: 4px;
margin-bottom: 0.5rem;
}
.card-list {
min-height: 100px;
}
}
Why :where()?
The :where() pseudo-class has zero specificity, which provides important benefits:
- Easy overrides: Global themes and utility classes can still override component styles
- No specificity wars: Avoids the escalating specificity problems common in large stylesheets
- Native browser support: Works in all modern browsers without compilation
Alternative: Higher Specificity with :is()
If you need your component styles to have higher specificity (to override external libraries), use :is() instead:
/* Higher specificity - harder to override */
:is([data-component="kanban-column"]) {
.header {
font-weight: bold;
}
}
Native CSS Nesting
Modern browsers support CSS nesting natively. Combined with attribute selectors, you get SCSS-like organization without any build step:
[data-component="task-card"] {
background: white;
border-radius: 8px;
padding: 1rem;
.title {
font-size: 1.1rem;
font-weight: 600;
}
.description {
color: #666;
margin-top: 0.5rem;
}
.priority {
display: inline-block;
padding: 0.25rem 0.5rem;
border-radius: 4px;
font-size: 0.75rem;
&.high { background: #fee2e2; color: #dc2626; }
&.medium { background: #fef3c7; color: #d97706; }
&.low { background: #d1fae5; color: #059669; }
}
}
Combining with data-bind-style
WildflowerJS's reactive style bindings work seamlessly with scoped CSS. Use CSS for static structure, and data-bind-style for dynamic values:
<!-- CSS handles structure, data-bind-style handles dynamic color -->
<div data-component="kanban-column">
<div class="header" data-bind-style="{ backgroundColor: currentColor }">
<span data-bind="column.name"></span>
</div>
</div>
:where([data-component="kanban-column"]) {
.header {
/* Static styles */
padding: 1rem;
font-weight: bold;
border-radius: 8px 8px 0 0;
/* backgroundColor comes from data-bind-style */
}
}
Computed Styles from State
For complex dynamic styling (like calculating text color based on background luminance), use computed properties:
wildflower.component('kanban-column', {
state: {
currentColor: '#ebecf0'
},
computed: {
textColor() {
// Calculate luminance for contrast
const hex = this.currentColor.replace('#', '');
const r = parseInt(hex.substr(0, 2), 16);
const g = parseInt(hex.substr(2, 2), 16);
const b = parseInt(hex.substr(4, 2), 16);
const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
return luminance > 0.5 ? '#000000' : '#ffffff';
}
}
});
<div class="header"
data-bind-style="{ backgroundColor: currentColor, color: textColor }">
...
</div>
Recommended Patterns
| Use Case | Approach |
|---|---|
| Component-scoped styles | :where([data-component="name"]) { ... } |
| Dynamic colors/sizes | data-bind-style with state/computed |
| Conditional classes | data-bind-class |
| Theming | CSS custom properties (--var-name) |
| Utility-first | Tailwind, Bootstrap, or similar (fully compatible) |
What We Don't Do
WildflowerJS intentionally avoids:
- Runtime CSS parsing: No JavaScript cycles spent processing
<style>tags - Auto-prefixed class names: Would cause Flash of Unstyled Content (FOUC)
- CSS-in-JS: Adds complexity and bundle size
- Build-time hashing: Requires compilation step
Browser Support
The patterns on this page work in all modern browsers:
:where()- Chrome 88+, Firefox 78+, Safari 14+:is()- Chrome 88+, Firefox 78+, Safari 14+- CSS Nesting - Chrome 120+, Firefox 117+, Safari 17.2+
For older browser support, use a PostCSS build step or stick with non-nested selectors.