Skip to content

Commit bfa4e24

Browse files
author
Frank van Viegen
committed
v1.7.1: Fix insertCss for nested union selectors
1 parent 876d65e commit bfa4e24

5 files changed

Lines changed: 49 additions & 19 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
# Changelog
22

3-
### 1.7.0 (2026-01-23)
3+
### 1.7.1 (2026-01-27)
4+
5+
**Fixes:**
6+
- `insertCss` now properly handles combining nested union selectors (`{"a, .link": {"svg, .icon": "color:red"}}`).
7+
8+
### 1.7.0 (2026-01-26)
49

510
**Breaking changes:**
611
- String syntax for `insertCss()` and `insertGlobalCss()`: Both functions now accept concise style strings using the same `property:value` syntax as inline CSS (e.g., `insertCss("bg:#f0f0f0 p:$3 r:8px")`).

README.md

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,29 @@
11
# [Aberdeen](https://aberdeenjs.org/) [![](https://img.shields.io/badge/license-ISC-blue.svg)](https://github.com/vanviegen/aberdeen/blob/master/LICENSE.txt) [![](https://badge.fury.io/js/aberdeen.svg)](https://badge.fury.io/js/aberdeen) ![](https://img.shields.io/bundlejs/size/aberdeen) [![](https://img.shields.io/github/last-commit/vanviegen/aberdeen)](https://github.com/vanviegen/aberdeen)
22

3-
Build fast reactive UIs in pure TypeScript/JavaScript without a virtual DOM.
3+
Reactive UIs in plain TypeScript. Simple to learn, fast to ship.
44

5-
Aberdeen's approach is refreshingly simple:
6-
7-
> Use many small anonymous functions for emitting DOM elements, and automatically rerun them when their underlying data changes. JavaScript `Proxy` is used to track reads and updates to this data, which can consist of anything, from simple values to complex, typed, and deeply nested data structures.
5+
Aberdeen wraps your state in ES6 `Proxy` objects for fine-grained property access tracking, then automatically re-executes only the DOM-building closures that depend on changed data. So we get precise DOM updates with neither virtual DOM diffing nor compiler magic.
86

97
## Why use Aberdeen?
108

11-
- 🎩 **Simple:** Express UIs naturally in JavaScript/TypeScript, without build steps or JSX, and with a minimal amount of concepts you need to learn.
12-
-**Fast:** No virtual DOM. Aberdeen intelligently updates only the minimal, necessary parts of your UI when proxied data changes.
13-
- 👥 **Awesome lists**: It's very easy and performant to reactively display data sorted by whatever you like.
14-
- 🔬 **Tiny:** Around 6KB (minimized and gzipped) for the core system. Zero runtime dependencies.
15-
- 🔋 **Batteries included**: Comes with browser history management, routing, revertible patches for optimistic user-interface updates, component-local CSS, SVG support, helper functions for transforming reactive data (mapping, partitioning, filtering, etc) and hide/unhide transition effects. No bikeshedding required!
9+
- **Simple:** Express UIs naturally in JavaScript/TypeScript, without requiring build steps or JSX, and with a minimal amount of concepts you need to learn.
10+
- **Type-safe:** Your reactive state can be regular TypeScript objects and arrays, with full type safety and autocompletion.
11+
- **Fast:** No virtual DOM. Aberdeen intelligently updates only the minimal, necessary parts of your UI when proxied data changes.
12+
- **Awesome lists**: It's very easy and performant to reactively display data sorted by whatever you like.
13+
- **Tiny:** Around 7KB (minimized and gzipped) for the core system. Zero runtime dependencies.
14+
- **Batteries included**: Comes with...
15+
- Browser history management
16+
- Routing
17+
- Revertible patches for optimistic user-interface updates
18+
- Component-local CSS with Tailwind-like shorthands
19+
- SVG support
20+
- Helper functions for transforming reactive data (mapping, partitioning, filtering, etc)
21+
- Hide/unhide transition effects
1622

1723
## Why *not* use Aberdeen?
1824

19-
- 🤷 **Lack of community:** There are not many of us -Aberdeen developers- yet, so don't expect terribly helpful Stack Overflow/AI answers.
20-
- 📚 **Lack of ecosystem:** You'd have to code things yourself, instead of duct-taping together a gazillion React ecosystem libraries.
25+
- **Lack of community:** There are not many of us -Aberdeen developers- yet, so don't expect terribly helpful Stack Overflow/AI answers.
26+
- **Lack of ecosystem:** You'd have to code things yourself, instead of duct-taping together a gazillion React ecosystem libraries.
2127

2228
## Examples
2329

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "aberdeen",
3-
"version": "1.7.0",
3+
"version": "1.7.1",
44
"author": "Frank van Viegen",
55
"main": "dist-min/aberdeen.js",
66
"devDependencies": {

src/aberdeen.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2353,6 +2353,16 @@ export function insertCss(style: string | object): string {
23532353
return prefix;
23542354
}
23552355

2356+
function combinePrefixSelector(prefix: string, key: string): string {
2357+
const sel = [];
2358+
for(const p of prefix.split(',')) {
2359+
for(const k of key.split(',')) {
2360+
sel.push(k.includes("&") ? k.trim().replace(/&/g, p) : `${p} ${k.trim()}`.trim());
2361+
}
2362+
}
2363+
return sel.join(',');
2364+
}
2365+
23562366
function objectToCss(style: object, prefix: string): string {
23572367
let css = "";
23582368

@@ -2364,19 +2374,15 @@ function objectToCss(style: object, prefix: string): string {
23642374
css += `${key}{\n${objectToCss(val, prefix)}}\n`;
23652375
} else {
23662376
// Regular nested selector
2367-
const sel = key === '&' ? prefix :
2368-
key.includes("&") ? key.replace(/&/g, prefix) :
2369-
`${prefix} ${key}`.trim();
2370-
css += objectToCss(val, sel);
2377+
css += objectToCss(val, combinePrefixSelector(prefix, key));
23712378
}
23722379
} else if (typeof val === 'string') {
23732380
if (key.startsWith("@")) {
23742381
// Media query with string value - wrap it
23752382
css += `${key}{\n${styleStringToCss(val, prefix)}}\n`;
23762383
} else {
23772384
// String value - parse as style string
2378-
const sel = key.includes("&") ? key.replace(/&/g, prefix) : `${prefix} ${key}`.trim();
2379-
css += styleStringToCss(val, sel);
2385+
css += styleStringToCss(val, combinePrefixSelector(prefix, key));
23802386
}
23812387
}
23822388
}

tests/insertCss.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,3 +137,16 @@ test('Grid template areas with multiple quoted strings', async () => {
137137
`${cls}{grid-template-areas:"header header" "sidebar content" "footer footer";}`,
138138
);
139139
});
140+
141+
test('Handles unions combined with further nested selectors', async () => {
142+
let cls = insertCss({
143+
'button, a.link': {
144+
'&': 'color:blue',
145+
'&:hover, &.active': 'color:darkblue'
146+
},
147+
});
148+
assertCss(
149+
`${cls} button,${cls} a.link{color:blue;}`,
150+
`${cls} button:hover,${cls} button.active,${cls} a.link:hover,${cls} a.link.active{color:darkblue;}`,
151+
);
152+
});

0 commit comments

Comments
 (0)