gulp-mu-css 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,774 @@
1
+ # Einführung
2
+
3
+ µCSS ist ein Node-Modul zur Verarbeitung von CSS-Dateien und zur Generierung von Web-Grafiken. Dieses Handbuch beschreibt die Version 2 — den Node-basierten Nachfolger des 2013 eingeführten, Adobe-Photoshop-basierten µCSS 1: Aus erweiterten Quell-Stylesheets (`.µ.css`) und den Medienquellen (z. B. PSD-Entwürfen) entstehen per Gulp die fertigen Skin-Dateien — Standard-CSS plus alle benötigten Bilder, Sprites, Cursor, Fonts und Sounds. Die Bilderzeugung übernimmt das Schwester-Modul µPS, das die alten PS-Plugins ohne Adobe-Abhängigkeit nachbildet.
4
+
5
+ Da das µ-Zeichen in Paket- und Repository-Namen (npm, git) immer wieder Probleme bereitet, lauten die technischen Namen `gulp-mu-css` und `gulp-mu-ps` — µCSS und µPS sind die Anzeigenamen.
6
+
7
+ Die wichtigsten Eigenschaften von µCSS 2:
8
+
9
+ - Quell-Stylesheets bleiben **syntaktisch valides CSS** — Editoren, Linter und Diff-Werkzeuge funktionieren unverändert.
10
+ - **Benannte CSS-Eigenschaften** über Skin-Variablen (`$.name`).
11
+ - **Berechnung von CSS-Werten durch JavaScript-Ausdrücke** direkt im Stylesheet — ohne Ersatzzeichen wie `«»¡`.
12
+ - **Mehrere Layouts (Skins)** aus denselben Quellen über Manifest-Dateien.
13
+ - **Automatische Sprite-Atlas-Generierung** inklusive Retina-Varianten (`@2x`) und `image-set()`.
14
+ - **Cursor-Verwaltung** mit Hotspot, Fallback und Retina-Unterstützung.
15
+ - **Vorladen von CSS-Bildern** über eine generierte Preload-Regel.
16
+ - **Automatische Bilderzeugung** aus PSD-Entwürfen (Buttons, Icons, App-Icons, Animations-Strips) via µPS.
17
+ - **Inkrementeller Build mit Cache** — nur Geändertes wird neu generiert.
18
+ - **Aussagekräftige Fehlermeldungen** mit Datei, Zeile und Quelltext-Ausschnitt.
19
+
20
+ Im Gegensatz zu µCSS 1 läuft Version 2 vollständig in Node.js (ab Version 18) und benötigt weder Photoshop noch andere Adobe-Produkte. Die Build-Steuerung erfolgt über Gulp oder direkt über die Node-API.
21
+
22
+ ## Einordnung: Warum µCSS?
23
+
24
+ Für fast jeden Teilaspekt von µCSS existiert ein etabliertes Einzelwerkzeug — aber kein System, das alles bündelt:
25
+
26
+ | Funktion | Nächstes existierendes Pendant | Was dort fehlt |
27
+ | :--- | :--- | :--- |
28
+ | Variablen & Farbfunktionen | Sass/LESS (`lighten()`, `mix()`), natives CSS (`color-mix()`, Custom Properties) | kein eingebettetes JavaScript |
29
+ | JavaScript in CSS-Werten | postcss-functions (registrierte JS-Funktionen als CSS-Funktionen) | nur benannte Funktionen — keine freien Ausdrücke, keine Direktiven mit Regel-/Dokument-Zugriff |
30
+ | Stylesheets mit voller JS-Power | vanilla-extract (Stylesheets-in-TypeScript) | umgekehrter Ansatz — das normale CSS-Format geht verloren |
31
+ | Sprite-Atlas aus CSS-Referenzen | spritesmith-Familie, postcss-sprites | weitgehend eingefroren, kein Retina-`image-set`-Workflow, kein gemeinsamer Cache |
32
+ | Bilderzeugung aus PSD-Entwürfen | nur Bausteine (`ag-psd`, Asset-Export aus Figma/Sketch) | kein Serien-Rendering mit Ebenenstil-Übertragung, nicht mit dem CSS-Build gekoppelt |
33
+ | Cursor, Preload, Skin-Manifest, Medien-Cache | handgestrickte Build-Skripte | nirgends gebündelt |
34
+
35
+ Ein Teil der ursprünglichen µCSS-Motivation von 2013 ist heute durch natives CSS gelöst (Custom Properties, `color-mix()`, Nesting) — deshalb verzichtet µCSS 2 bewusst auf LESS-Unterstützung und Vendor-Prefixes. Der verbleibende Kern ist konkurrenzlos: beliebige JavaScript-Ausdrücke im CSS plus Direktiven mit AST-Zugriff, gekoppelt mit Sprite-Atlas inklusive Retina, PSD-Render-Pipeline und inkrementellem Cache, gesteuert über ein Manifest pro Skin. Und da µCSS intern eine PostCSS-Pipeline ist, bleibt das gesamte PostCSS-Ökosystem (cssnano, Stylelint, …) andockbar, statt in Konkurrenz dazu zu stehen.
36
+
37
+ # Installation und Start
38
+
39
+ µCSS und µPS sind als npm-Module `gulp-mu-css` und `gulp-mu-ps` verfügbar. Die Installation erfolgt im eigenen Projekt:
40
+
41
+ ```
42
+ npm install gulp-mu-css gulp-mu-ps
43
+ ```
44
+
45
+ µCSS hängt von µPS ab (Atlas- und Bilderzeugung) — nicht umgekehrt. Beide Module sind separat verwendbar.
46
+
47
+ Der typische Einstieg ist ein Gulp-Task, der ein Skin-Manifest baut:
48
+
49
+ ```js
50
+ // gulpfile.mjs
51
+ import gulp from "gulp";
52
+ import { BuildSkin } from "gulp-mu-css";
53
+
54
+ export async function SkinStd() {
55
+ await BuildSkin("skins/src/std.µcss.mjs");
56
+ }
57
+ export function SkinWatch() {
58
+ gulp.watch(["skins/src/**", "dev/media/final/**"], SkinStd);
59
+ }
60
+ ```
61
+
62
+ Der Aufruf `npx gulp SkinStd` kompiliert den Skin „std" in das Verzeichnis `skins/std/`. `BuildSkin` ist idempotent und cache-gestützt: Ein zweiter Aufruf ohne Änderungen ist nach wenigen Millisekunden fertig (siehe Kapitel „Build-Ablauf").
63
+
64
+ # Anwendungsfälle
65
+
66
+ Die folgenden Abschnitte zeigen die typischen Einsatzszenarien — analog zu den Use Cases des alten µCSS-Handbuchs, aber in der neuen Syntax.
67
+
68
+ ## Benannte Eigenschaften (Variablen)
69
+
70
+ Der einfachste Anwendungsfall: Ein Wert wird einmal im Skin-Manifest benannt und an beliebig vielen Stellen verwendet. Im Manifest:
71
+
72
+ ```js
73
+ // skins/src/std.µcss.mjs
74
+ import { DefineSkin } from "gulp-mu-css";
75
+
76
+ export default DefineSkin({
77
+ vars: {
78
+ textColor: "#ff0000",
79
+ backColor: "#0000ff"
80
+ },
81
+ files: [{ source: "src.µ.css", target: "std.css" }]
82
+ });
83
+ ```
84
+
85
+ Im Quell-Stylesheet `src.µ.css`:
86
+
87
+ ```css
88
+ div.mydiv {
89
+ padding: 5px;
90
+ font-size: 24px;
91
+ color: µ($.textColor);
92
+ background-color: µ($.backColor);
93
+ border: 10px µ($.backColor) solid;
94
+ border-radius: 10px;
95
+ }
96
+ ```
97
+
98
+ Kompiliert nach `std.css`:
99
+
100
+ ```css
101
+ div.mydiv {
102
+ padding: 5px;
103
+ font-size: 24px;
104
+ color: #ff0000;
105
+ background-color: #0000ff;
106
+ border: 10px #0000ff solid;
107
+ border-radius: 10px;
108
+ }
109
+ ```
110
+
111
+ Anders als beim alten µCSS sind keine Platzhalter-Properties und keine `Set*`-Direktiven mehr nötig: Der Wert steht direkt dort, wo er hingehört.
112
+
113
+ ## Berechnungen und Ausdrücke
114
+
115
+ Der Inhalt von `µ(...)` ist ein beliebiger JavaScript-Ausdruck. Damit sind Berechnungen, Bedingungen und Farboperationen direkt im Stylesheet möglich:
116
+
117
+ ```css
118
+ td.subheadline {
119
+ border-bottom: 1px dashed µ(Lighten($.selectBaseBrgdColor, 0.7));
120
+ }
121
+ div.panel {
122
+ padding: µ(Math.floor($.basePadding * 0.2))px;
123
+ background-color: µ(Alpha($.baseBrgdColor, 0.85));
124
+ z-index: µ($.PanelZIndex + 10);
125
+ }
126
+ div.hint {
127
+ color: µ($.darkMode ? "#e0e0e0" : "#202020");
128
+ }
129
+ ```
130
+
131
+ Für Operationen, die mehrere Properties erzeugen oder die Regel als Ganzes verändern, gibt es Direktiven (`-µ:`), zum Beispiel mit eigenen Makro-Funktionen aus dem Manifest:
132
+
133
+ ```css
134
+ div.menu {
135
+ -µ: Borders($.menuBaseBrgdColor, 1, 0.3, -0.3, -0.3, 0.3);
136
+ }
137
+ ```
138
+
139
+ Die Direktive ruft die Helper-Funktion `Borders` auf, die vier `border-*`-Properties berechnet und in die Regel einfügt (siehe Kapitel „µ-Auswertungskontext").
140
+
141
+ ## Mehrere Layouts über Skin-Manifeste
142
+
143
+ Das alte Template-Compiling (eine Vorlagen-CSS plus je eine `µ.layout.css` pro Layout) wird durch mehrere Manifeste ersetzt, die sich dieselben `.µ.css`-Quellen teilen:
144
+
145
+ ```js
146
+ // skins/src/layout_a.mjs
147
+ import { DefineSkin } from "gulp-mu-css";
148
+ export default DefineSkin({
149
+ vars: { textColor: "#ff0000", backColor: "#0000ff" },
150
+ files: [{ source: "template.µ.css", target: "layout_a.css" }]
151
+ });
152
+ ```
153
+
154
+ ```js
155
+ // skins/src/layout_b.mjs
156
+ import { DefineSkin } from "gulp-mu-css";
157
+ export default DefineSkin({
158
+ vars: { textColor: "#ff00ff", backColor: "#00ff00" },
159
+ files: [{ source: "template.µ.css", target: "layout_b.css" }]
160
+ });
161
+ ```
162
+
163
+ Jedes Manifest erzeugt ein eigenes Ausgabeverzeichnis (`skins/layout_a/`, `skins/layout_b/`) mit eigener CSS, eigenen Bildern und eigenem Build-Cache. Gemeinsame Variablen lassen sich als normales JavaScript-Modul importieren und in beiden Manifesten verwenden.
164
+
165
+ ## Automatische Sprite-Generierung
166
+
167
+ Eines der Highlights von µCSS (in Version 1 wie in Version 2) ist die automatische Erzeugung von Sprite-Atlanten. Die Direktive `Sprite(url)` registriert das Bild einer Regel für den Atlas:
168
+
169
+ ```css
170
+ div.loginbutton {
171
+ display: inline-block;
172
+ -µ: Sprite("imgs/aqua/but_login_normal.png");
173
+ }
174
+ div.loginbutton:hover {
175
+ display: inline-block;
176
+ -µ: Sprite("imgs/aqua/but_login_hover.png");
177
+ }
178
+ div.helpbutton {
179
+ display: inline-block;
180
+ -µ: Sprite("imgs/aqua/but_help_normal.png");
181
+ }
182
+ ```
183
+
184
+ Beim Build werden alle registrierten Bilder in ein einziges Atlas-Bild gepackt (`imgs/sprites.png`, dazu `imgs/sprites@2x.png` aus den `@2x`-Quellbildern) und die Regeln umgeschrieben:
185
+
186
+ ```css
187
+ div.loginbutton {
188
+ display: inline-block;
189
+ background-image: url(imgs/sprites.png);
190
+ background-image: image-set(url(imgs/sprites.png)1x, url(imgs/sprites@2x.png)2x);
191
+ background-repeat: no-repeat;
192
+ background-position: 0px 0px;
193
+ width: 55px;
194
+ height: 55px;
195
+ }
196
+ ```
197
+
198
+ `width`, `height`, `background-position` und `background-repeat` werden automatisch gesetzt; vorhandene Deklarationen dieser Properties in der Regel werden ersetzt. Anders als beim alten µCSS entfallen die Vendor-Prefix-Varianten (`-webkit-image-set` usw.) — alle relevanten Browser unterstützen `image-set()` seit Jahren unprefixt.
199
+
200
+ Identische Quellbilder teilen sich automatisch eine Atlas-Position (Duplikat-Reduktion). Der Atlas wird nur neu gepackt, wenn sich die Bildmenge oder ein Quellbild geändert hat.
201
+
202
+ ## Bilder vorladen (Preload)
203
+
204
+ Wichtige Bilder (z. B. Hover-Zustände und Cursor) können beim Laden der Seite vorab geladen werden. µCSS sammelt die Bild-URLs und erzeugt eine Preload-Regel, wenn die Manifest-Option `sprites.preloadRule` gesetzt ist:
205
+
206
+ ```css
207
+ div.csspreload {
208
+ background-image: url(imgs/sprites.png), url(imgs/general/gui/cursors/zoom.png);
209
+ display: none;
210
+ }
211
+ ```
212
+
213
+ In der HTML-Seite genügt ein leeres Element mit dieser Klasse:
214
+
215
+ ```html
216
+ <div class="csspreload"></div>
217
+ ```
218
+
219
+ Cursor-Bilder aus den `cursors`-Definitionen werden automatisch in die Preload-Liste aufgenommen.
220
+
221
+ ## Cursor mit Hotspot und Fallback
222
+
223
+ Cursor werden im Manifest definiert und im Stylesheet per Name verwendet. Definition:
224
+
225
+ ```js
226
+ cursors: [
227
+ { name: "zoom", fallback: "zoom-in", image: "imgs/general/gui/cursors/zoom.png", hotspot: [10, 8] },
228
+ { name: "wait", fallback: "wait", image: "imgs/general/gui/cursors/wait.png", hotspot: [12, 12] }
229
+ ]
230
+ ```
231
+
232
+ Verwendung als Direktive oder als Wertform:
233
+
234
+ ```css
235
+ *.cursor_zoom {
236
+ -µ: Cursor("zoom");
237
+ }
238
+ a.preview {
239
+ cursor: µ(Cursor("zoom"));
240
+ }
241
+ ```
242
+
243
+ Die Direktive erzeugt die `url()`-Form mit Hotspot und Fallback sowie — wenn eine `@2x`-Bilddatei existiert — zusätzlich die `image-set()`-Variante:
244
+
245
+ ```css
246
+ *.cursor_zoom {
247
+ cursor: url(imgs/general/gui/cursors/zoom.png) 10 8, zoom-in;
248
+ cursor: image-set(url(imgs/general/gui/cursors/zoom.png)1x, url(imgs/general/gui/cursors/zoom@2x.png)2x) 10 8, zoom-in;
249
+ }
250
+ ```
251
+
252
+ ## Automatische Bilderzeugung (media-Steps)
253
+
254
+ Was früher die µCSS-Plugins (ButtonCreator, AppIconMaker, FileCopy) erledigt haben, übernehmen jetzt die `media`-Einträge im Manifest. Sie laufen vor der CSS-Kompilierung und erzeugen bzw. kopieren alle Medien in das Skin-Verzeichnis:
255
+
256
+ ```js
257
+ media: [
258
+ { buttonsAndIcons: "dev/media/final/general/gui/panelbuttons.psd", layout: "std", outputDir: "imgs/general/gui/panelbuttons" },
259
+ { buttonsAndIcons: "dev/media/final/general/gui/cursors.psd", layout: "std", outputDir: "imgs/general/gui/cursors" },
260
+ { appIcons: "dev/media/final/favicon.psd", layout: "std", profiles: ["web"] },
261
+ { sequenceStrip: "dev/media/raw/glittery/imgs", outputFile: "imgs/general/gui/glittery/glittery.png" },
262
+ { copy: "dev/media/final/general/gui/teaserbgrd.png", to: "imgs/general/gui" },
263
+ { copyFolder: "dev/media/final/fonts", to: "fonts" }
264
+ ]
265
+ ```
266
+
267
+ - **`buttonsAndIcons`** rendert aus einem geschichteten PSD-Entwurf (Layouts × Icon-Glyphen) komplette Button- und Icon-Serien inklusive `@2x`-Varianten — der Nachfolger des ButtonCreator-Plugins. Die Fülloptionen (Schlagschatten, Verläufe, Schein usw.) werden von microPS nachgebildet.
268
+
269
+ ![Dieselben Icon-Glyphen, gerendert mit zwei Layouts desselben Entwurfsdokuments („alu" und „aqua")](imgs/bc_alu.png)
270
+
271
+ ![](imgs/bc_aqua.png)
272
+
273
+ Mit `mode: "topLayerSets"` arbeitet der Step im zweiten Legacy-Modus (`CreateByTopLayerSets`): Statt der Icon×State-Matrix wird **jede direkte Untergruppe der Layout-Gruppe zu genau einem Bild**, benannt nach dem Gruppennamen. Eine `icons`-Gruppe ist hier nicht nötig — typisch für Logos, Animationsframes (z. B. `activityindicator`) und Emoticons. Optional grenzt `setPattern` (Regex-String) die exportierten Gruppen ein.
274
+
275
+ ```js
276
+ { buttonsAndIcons: "dev/media/final/general/activityindicator.psd", layout: "std",
277
+ mode: "topLayerSets", outputDir: "imgs/general" }
278
+ ```
279
+
280
+ - **`appIcons`** erzeugt App-Icons und Favicons profilbasiert (`web`, `ios`, `play`) aus einem quadratischen Master — der modernisierte AppIconMaker.
281
+ - **`sequenceStrip`** baut aus einer Bildsequenz (Einzeldateien oder ein Bild im DSD-Format) einen horizontalen Animations-Strip mit JSON-Frame-Daten:
282
+
283
+ ![Animations-Strip, generiert aus 19 Einzel-Frames einer Rauch-Animation](imgs/strip_result_smoke.png)
284
+ - **`copy` / `copyFolder`** kopieren statische Dateien (Make-artig: nur wenn die Quelle neuer ist).
285
+
286
+ Die Details der Bildgeneratoren (Ebenenstruktur der Entwürfe, DSD-Format, Profile) beschreibt die µPS-Dokumentation.
287
+
288
+ ### Zwischenschritte raw → final (`outputBase`)
289
+
290
+ Standardmäßig schreiben alle Steps in das Skin-Ausgabeverzeichnis. Mit `outputBase: "project"` schreibt ein Step stattdessen relativ zum Projektstamm — damit lässt sich die Erzeugung von Zwischenergebnissen (z. B. Sequenz-Strips aus `dev/media/raw/...` nach `dev/media/final/...`) direkt im Manifest abbilden. Die Steps laufen in Manifest-Reihenfolge, ein nachfolgender `copy`/`copyFolder`-Step übernimmt das Ergebnis dann wie jedes andere Final-Asset in den Skin:
291
+
292
+ ```js
293
+ media: [
294
+ // 1. Strip aus den Roh-Frames in den final-Baum generieren ...
295
+ { sequenceStrip: "dev/media/raw/flyex/frames", outputFile: "dev/media/final/general/gui/flyex/flyex.png", outputBase: "project" },
296
+ // 2. ... und von dort wie gewohnt in den Skin kopieren.
297
+ { copyFolder: "dev/media/final/general/gui/flyex", to: "imgs/general/gui/flyex", filter: "\\.(png|json)$" }
298
+ ]
299
+ ```
300
+
301
+ Auch für diese Steps gilt der inkrementelle Build: Generiert wird nur, wenn das Ergebnis fehlt oder sich Konfiguration bzw. Quellen (mtime/Größe) geändert haben. Alternativ kann der raw-→-final-Schritt natürlich weiterhin als eigener Gulp-Task vor dem µCSS-Build laufen — `outputBase: "project"` ist der Weg, wenn alles in einer Manifest-Datei stehen soll.
302
+
303
+ # Grundideen von µCSS
304
+
305
+ ## Das .µ.css-Format
306
+
307
+ Quell-Stylesheets heißen `*.µ.css`. Das Doppelsuffix sorgt dafür, dass Editoren die Dateien als normales CSS erkennen und das Syntax-Highlighting ohne weitere Konfiguration funktioniert. Für den Compiler ist die Endung irrelevant — das Manifest referenziert die Quellen explizit; wer das µ-Zeichen in Dateinamen vermeiden möchte, kann ebenso `*.mu.css` verwenden. Inhaltlich ist eine `.µ.css`-Datei Standard-CSS mit genau zwei Erweiterungspunkten — beide sind syntaktisch valides CSS, so dass Editoren und Linter normal funktionieren:
308
+
309
+ 1. **Werte-Interpolation** `µ(Ausdruck)` — überall, wo ein CSS-Wert steht.
310
+ 2. **Direktiven** `-µ: Ausdruck;` — als Deklaration innerhalb einer Regel.
311
+
312
+ Wer das µ-Zeichen nicht auf der Tastatur hat, verwendet die ASCII-Aliasse `mu(...)` und `-mu:` — beide Formen sind gleichwertig.
313
+
314
+ Anders als beim alten µCSS gibt es **keine Steuer-Regeln** (`::-µcss-init` usw.) mehr im Stylesheet: Alles, was die Kompilierung steuert (Variablen, Cursor-Definitionen, Medienerzeugung, Dateizuordnung), steht im Skin-Manifest — einer normalen JavaScript-Datei (siehe unten).
315
+
316
+ ## Werte-Interpolation µ(Ausdruck)
317
+
318
+ `µ(...)` ist aus CSS-Sicht eine unbekannte, aber valide Funktion. Beim Kompilieren wird der Inhalt als JavaScript-Ausdruck ausgewertet und das `µ(...)`-Vorkommen durch das Ergebnis ersetzt:
319
+
320
+ ```css
321
+ ::selection {
322
+ background-color: µ($.selectBaseBrgdOnDarkColor);
323
+ color: µ($.selectBaseTextColor);
324
+ }
325
+ ```
326
+
327
+ Mehrere Interpolationen pro Wert sind erlaubt (`padding: µ($.padY)px µ($.padX)px;`), ebenso Interpolationen in At-Rule-Parametern (z. B. `@media (max-width: µ($.breakpoint)px)`). Liefert ein Ausdruck `null` oder `undefined`, bricht die Kompilierung mit einer Fehlermeldung ab — so fallen Tippfehler in Variablennamen sofort auf.
328
+
329
+ Diese eine Erweiterung ersetzt die komplette `Set*`-Familie des alten µCSS (`SetColor`, `SetBackgroundColor`, `SetZIndex`, `SetWidth`, `AddProperty` für einfache Werte usw.): Die Property steht wieder an ihrem Platz, ein Platzhalter-Wert ist nicht mehr nötig.
330
+
331
+ ## Direktiven -µ: Ausdruck
332
+
333
+ Direktiven sind Deklarationen mit dem Property-Namen `-µ` (oder `-mu`). Ihr Wert ist ein einzelner JavaScript-Ausdruck, der mit Zugriff auf die umgebende Regel und das Dokument ausgeführt wird. Die Direktive selbst wird aus der Ausgabe entfernt:
334
+
335
+ ```css
336
+ div.panel.modal div.companylogo {
337
+ -µ: Sprite("imgs/logos/dosing_logo.png");
338
+ margin-left: auto;
339
+ }
340
+ *.cursor_zoom {
341
+ -µ: Cursor("zoom");
342
+ }
343
+ div.content.glittery {
344
+ -µ: Sprite("imgs/general/gui/glittery/glittery.png", { afterWork: GlitterySprite });
345
+ }
346
+ ```
347
+
348
+ Das `µ.`-Präfix des alten µCSS entfällt — der Kontext ist implizit. Direktiven werden in Dokumentreihenfolge ausgewertet.
349
+
350
+ ## Das Skin-Manifest
351
+
352
+ Pro Skin (CSS-Thema) liegt im Quellverzeichnis eine Manifest-Datei `<skinname>.µcss.mjs` — normales JavaScript (ES6+), importierbar und testbar. Das Doppelsuffix `.µcss.mjs` (ASCII-Alternative `.mucss.mjs`) kennzeichnet die Datei eindeutig als µCSS-Skin-Manifest, damit sie nicht mit einem gewöhnlichen Modul oder Gulpfile verwechselt wird; der Skin-Name ergibt sich aus dem Teil davor (`std.µcss.mjs` → Skin `std`). Sie ersetzt die alte Steuerdatei `µ.std.css` vollständig:
353
+
354
+ ```js
355
+ // skins/src/std.µcss.mjs — Skin "std", Ziel: skins/std/
356
+ import { DefineSkin } from "gulp-mu-css";
357
+ import { GlitterySprite, FlyEx, Borders, TableBackgrounds } from "./helpers.mjs";
358
+
359
+ export default DefineSkin({
360
+ vars: {
361
+ baseBrgdColor: "#202020",
362
+ selectBaseBrgdColor: "#007570",
363
+ PanelZIndex: 9000
364
+ },
365
+ helpers: { GlitterySprite, FlyEx, Borders, TableBackgrounds },
366
+ cursors: [
367
+ { name: "zoom", fallback: "zoom-in", image: "imgs/general/gui/cursors/zoom.png", hotspot: [10, 8] }
368
+ ],
369
+ media: [
370
+ { buttonsAndIcons: "dev/media/final/general/gui/panelbuttons.psd", layout: "std", outputDir: "imgs/general/gui/panelbuttons" },
371
+ { copyFolder: "dev/media/final/fonts", to: "fonts" }
372
+ ],
373
+ imageFormat: "png",
374
+ sprites: { file: "imgs/sprites.png", retina: true, preloadRule: true },
375
+ files: [
376
+ { source: "src.µ.css", target: "std.css" },
377
+ { source: "src_tinymce.µ.css", target: "std_tinymce.css" }
378
+ ]
379
+ });
380
+ ```
381
+
382
+ Der Skin-Name ergibt sich aus dem Dateinamen des Manifests, das Ausgabeverzeichnis aus dem Skin-Namen. Die vollständige Feld-Referenz steht im Kapitel „Manifest-Referenz".
383
+
384
+ ## JavaScript ohne Ersatzzeichen
385
+
386
+ Bei µCSS 1 mussten die Zeichen `{`, `}` und `;` in JavaScript-Anweisungen durch `«`, `»` und `¡` ersetzt werden, um CSS-Syntaxkonflikte zu vermeiden. Dieses Muster entfällt in Version 2 ersatzlos:
387
+
388
+ - In `µ(...)` und `-µ: ...` stehen **einzelne Ausdrücke** — der Parser zählt Klammern und berücksichtigt Strings, so dass auch Objekt-Literale (`{ afterWork: ... }`) und verschachtelte Aufrufe problemlos funktionieren.
389
+ - **Mehrzeilige Logik** (Schleifen, Funktionsdefinitionen) gehört nicht ins Stylesheet, sondern in die Helper-Module des Manifests — echte `.mjs`-Dateien mit Syntax-Highlighting, Linting und Debugger.
390
+
391
+ # Build-Ablauf und Gulp-Integration
392
+
393
+ Das alte µCSS wurde über ein modales Dialogfenster in Photoshop bedient. An dessen Stelle treten `BuildSkin` und Gulp: Der Build ist ein normaler, skriptbarer Node-Prozess — geeignet für Watch-Modus, CI und Automatisierung.
394
+
395
+ ## Verzeichniskonventionen
396
+
397
+ Für ein Manifest `skins/src/std.µcss.mjs` gilt (alle Pfade überschreibbar):
398
+
399
+ | Pfad | Bedeutung |
400
+ | :--- | :--- |
401
+ | `skins/src/` | Quellverzeichnis: Manifeste, `.µ.css`-Dateien, Helper-Module |
402
+ | `skins/std/` | Ausgabeverzeichnis des Skins „std": CSS, Bilder, Fonts, Sounds |
403
+ | `skins/std/.cache/build.json` | Build-Cache des Skins (Fingerprints, Atlas-Positionen) |
404
+ | Projektstamm | Zwei Ebenen über dem Manifest; Basis für `media`-Quellpfade wie `dev/media/...` |
405
+
406
+ `BuildSkin(manifestPfad, optionen)` akzeptiert `{ outputDir, rootDir, force }` zum Übersteuern.
407
+
408
+ ## Kompilierungs-Ablauf
409
+
410
+ Ein `BuildSkin`-Lauf führt fünf Schritte aus:
411
+
412
+ 1. **media-Steps**: Alle Bilder werden erzeugt bzw. kopiert (microPS) — sie müssen existieren, bevor Atlas und Cursor-Prüfungen laufen.
413
+ 2. **Kompilierung**: Jede `files`-Quelle wird geparst (PostCSS); Direktiven und Interpolationen werden in Dokumentreihenfolge ausgewertet. `Sprite()`-/`Cursor()`-Aufrufe registrieren zunächst nur ihre Bildreferenzen.
414
+ 3. **Sprite-Atlas**: Alle registrierten Bilder werden gepackt (inkl. `@2x`); danach werden die betroffenen Regeln umgeschrieben.
415
+ 4. **Hooks**: `afterWork`-Callbacks laufen mit Atlas-Ergebnis und AST-Zugriff.
416
+ 5. **Ausgabe**: Die kompilierten CSS-Dateien werden in das Skin-Verzeichnis geschrieben, der Cache wird gespeichert.
417
+
418
+ ## Inkrementeller Build und Cache
419
+
420
+ Jeder Lauf erzeugt nur das neu, was sich tatsächlich geändert hat. Primärer Mechanismus sind Datei-Modifikations-Zeitstempel (`mtime` plus Dateigröße) — bei großen PSD-Quellen um Größenordnungen schneller als Inhalts-Hashes:
421
+
422
+ - **Generator-Steps** (`buttonsAndIcons`, `appIcons`, `sequenceStrip`) werden übersprungen, wenn ihre Konfiguration unverändert ist, alle Quelldateien unveränderte Fingerprints haben und alle Ausgabedateien noch existieren. PSD-Rendering ist der teuerste Posten des Builds — hier zahlt der Cache am meisten.
423
+ - **`copy`/`copyFolder`** arbeiten Make-artig: kopiert wird nur, wenn die Quelle neuer als das Ziel ist.
424
+ - **Der Sprite-Atlas** wird nur neu gepackt, wenn sich die Bildmenge oder ein Quellbild (inkl. `@2x`) geändert hat — sonst werden die gespeicherten Positionen wiederverwendet und nur die CSS-Regeln damit umgeschrieben.
425
+ - **Die CSS-Kompilierung** selbst ist billig und läuft im Zweifel immer.
426
+
427
+ Der Cache liegt pro Skin in `<outputDir>/.cache/build.json` und trägt die Versionsnummern von µCSS und das Cache-Schema; bei Abweichung wird er verworfen (Vollbuild). `BuildSkin(manifest, { force: true })` oder das Löschen von `.cache/` erzwingt den Vollbuild explizit.
428
+
429
+ ## Gulp-Tasks und Watch
430
+
431
+ Ein Task pro Skin genügt; `BuildSkin` ist re-entrant:
432
+
433
+ ```js
434
+ import gulp from "gulp";
435
+ import { BuildSkin } from "gulp-mu-css";
436
+
437
+ export async function SkinStd() {
438
+ const report = await BuildSkin("skins/src/std.µcss.mjs");
439
+ console.log(`${report.skin}: ${report.files.length} Dateien, Atlas ${report.atlasSkipped ? "aus Cache" : "neu gepackt"}, ${report.duration} ms`);
440
+ }
441
+ export function SkinWatch() {
442
+ gulp.watch(["skins/src/**/*.µ.css", "skins/src/*.mjs", "dev/media/final/**"], SkinStd);
443
+ }
444
+ ```
445
+
446
+ Der Rückgabewert von `BuildSkin` ist ein Report mit `skin`, `outputDir`, `media` (Step-Ergebnisse mit `skipped`-Flag), `files`, `atlas`, `atlasSkipped` und `duration`.
447
+
448
+ # Manifest-Referenz (DefineSkin)
449
+
450
+ `DefineSkin(konfiguration)` deklariert die Skin-Konfiguration (Default-Export des Manifests) und validiert die Grundstruktur. Alle Felder sind optional, sofern nicht anders angegeben.
451
+
452
+ ## vars
453
+
454
+ Objekt mit den Skin-Variablen — der Ersatz für `µ.$.*`. In `.µ.css`-Ausdrücken als `$.name` zugreifbar, in Helper-Funktionen als `this.$.name`. Werte sind beliebige JavaScript-Werte (Strings, Zahlen, Objekte, auch berechnete).
455
+
456
+ ```js
457
+ vars: { baseBrgdColor: "#202020", PanelZIndex: 9000, icon_pencil: "\\e91c" }
458
+ ```
459
+
460
+ ## helpers
461
+
462
+ Objekt mit benutzerdefinierten Funktionen (Makros und Hooks). Sie stehen in `.µ.css`-Ausdrücken unter ihrem Namen zur Verfügung. Funktionen, die mit `function` (nicht als Arrow-Function) deklariert sind, erhalten beim Aufruf `this` = Auswertungs-Scope (siehe Kapitel „µ-Auswertungskontext").
463
+
464
+ ```js
465
+ import { Borders, GlitterySprite } from "./helpers.mjs";
466
+ // ...
467
+ helpers: { Borders, GlitterySprite }
468
+ ```
469
+
470
+ ## cursors
471
+
472
+ Liste der Cursor-Definitionen — der Ersatz für `µ.DefCursor`:
473
+
474
+ | Feld | Default | Bedeutung |
475
+ | :--- | :--- | :--- |
476
+ | `name` | — (Pflicht) | Name, unter dem der Cursor mit `Cursor("name")` verwendet wird. |
477
+ | `fallback` | = `name` | Standard-Cursor-Name (W3C) als Fallback. |
478
+ | `image` | `""` | Bild-URL relativ zum Skin-Verzeichnis; leer = Definition nur als Fallback-Mapping. |
479
+ | `hotspot` | `[0, 0]` | Klickpunkt `[x, y]` im Bild; `0,0` wird in der Ausgabe weggelassen. |
480
+ | `forceFallback` | `false` | Hängt den Fallback zusätzlich als letzte `cursor`-Deklaration an. |
481
+
482
+ Cursor-Bilder werden automatisch in die Preload-Liste aufgenommen.
483
+
484
+ ## media
485
+
486
+ Liste der Medien-Steps; sie laufen in der angegebenen Reihenfolge vor der Kompilierung. Der Step-Typ ergibt sich aus dem Schlüsselfeld:
487
+
488
+ | Step | Pflichtfelder | Weitere Felder | Wirkung |
489
+ | :--- | :--- | :--- | :--- |
490
+ | `buttonsAndIcons` | Quell-PSD, `layout`, `outputDir` | `retina` (Default `true`), `format`, `mode`, `setPattern` | Button-/Icon-Serien aus einem Entwurfsdokument rendern (microPS ButtonAndIconCreator). `mode: "topLayerSets"` schaltet auf „ein Bild pro Top-Untergruppe" um (Legacy-`CreateByTopLayerSets`, ohne `icons`-Gruppe — für Logos, Animationsframes, Emoticons); `setPattern` (Regex-String) filtert dabei die exportierten Gruppen. |
491
+ | `appIcons` | Quell-PSD/PNG | `outputDir`, `profiles`, `layout`, `background`, `appName`, `shortName`, `themeColor` | App-Icons/Favicons profilbasiert generieren (microPS AppIconMaker). |
492
+ | `sequenceStrip` | Quelle (Ordner oder DSD-Bild), `outputFile` | `retina` (Default `true`), `writeMapFile` (Default `true`), `format` | Horizontalen Animations-Strip plus JSON-Frame-Daten erzeugen (microPS SequenceStrip). |
493
+ | `copy` | Quelldatei | `to` (Zielordner, Default Skin-Wurzel) | Einzeldatei kopieren, wenn die Quelle neuer ist. |
494
+ | `copyFolder` | Quellordner | `to` (Default = Ordnername), `filter` (Regex-String, z. B. `"\\.(woff2?\|ttf)$"`) | Ordner rekursiv kopieren (Make-artig), optional gefiltert. |
495
+
496
+ Quellpfade sind relativ zum Projektstamm, Zielpfade relativ zum Skin-Verzeichnis. Jeder Step akzeptiert zusätzlich `outputBase: "skin"` (Default) oder `"project"`: Mit `"project"` ist der Zielpfad relativ zum Projektstamm — für Zwischenschritte wie raw → final (siehe Kapitel „Automatische Bilderzeugung").
497
+
498
+ ## imageFormat
499
+
500
+ Globaler Bildformat-Schalter: `"png"` (Default) oder `"webp"`. Gilt für den Sprite-Atlas und alle bilderzeugenden media-Steps; einzelne Steps können das Format per eigenem `format`-Feld übersteuern. Direkt referenzierte Bestandsbilder (`url(...)` in den Quellen, `copy`-Steps) bleiben unangetastet.
501
+
502
+ ## sprites
503
+
504
+ Optionen des Sprite-Atlas — der Ersatz für `µ.options.sprites.*`:
505
+
506
+ | Feld | Default | Bedeutung |
507
+ | :--- | :--- | :--- |
508
+ | `file` | `"imgs/sprites.png"` | Atlas-Datei (URL, wie sie in der CSS erscheint). Bei `imageFormat: "webp"` wird die Endung automatisch getauscht. |
509
+ | `retina` | `true` | Erzeugt zusätzlich `<name>@2x` aus den `@2x`-Quellbildern (müssen neben den 1x-Quellen liegen). |
510
+ | `padding` | `0` | Abstand in Pixeln zwischen den Sprites. |
511
+ | `preloadRule` | `false` | Erzeugt die `div.csspreload`-Regel im ersten Stylesheet. |
512
+
513
+ ## files
514
+
515
+ Liste der zu kompilierenden Stylesheets — der Ersatz für `µ.DependentCSSFile`. `source` ist relativ zum Quellverzeichnis, `target` relativ zum Skin-Verzeichnis; beide Felder sind Pflicht:
516
+
517
+ ```js
518
+ files: [
519
+ { source: "src.µ.css", target: "std.css" },
520
+ { source: "src_tinymce.µ.css", target: "std_tinymce.css" }
521
+ ]
522
+ ```
523
+
524
+ Alle Dateien eines Skins teilen sich Variablen, Helpers und den Sprite-Atlas. Die Preload-Regel landet im ersten Stylesheet.
525
+
526
+ # µ-Auswertungskontext (Referenz)
527
+
528
+ Jeder `µ(...)`-Ausdruck und jede `-µ:`-Direktive wird in einem Scope ausgewertet, der die folgenden Bindungen enthält. Er entspricht dem alten globalen `µ`-Objekt — nur ohne Präfix.
529
+
530
+ ## Das $-Objekt
531
+
532
+ `$` enthält die `vars` des Manifests. Lesen und Schreiben ist möglich; Änderungen gelten für den weiteren Verlauf der Kompilierung (Dokumentreihenfolge, über alle `files` eines Skins hinweg).
533
+
534
+ ```css
535
+ div.box { z-index: µ($.PanelZIndex + 1); }
536
+ ```
537
+
538
+ ## Farb- und Wert-Funktionen
539
+
540
+ | Funktion | Beschreibung |
541
+ | :--- | :--- |
542
+ | `Lighten(color, step, model = "hsl")` | Hellt eine Farbe relativ auf (positiver `step`) oder dunkelt sie ab (negativer `step`): `L' = clamp(L + L · step)`. Das Modell `"hsl"` ist bitgenau zum alten µCSS; `"oklch"` skaliert wahrnehmungsgleichmäßig. Alpha bleibt erhalten. |
543
+ | `Alpha(color, alpha)` | Ersetzt den Alpha-Kanal einer Farbe. `alpha` nach den Regeln aus „Arbeiten mit Farben". |
544
+ | `MixColors(color1, color2)` | Kanalweiser Mittelwert zweier Farben (inklusive Alpha). |
545
+ | `AlphaValue(alpha)` | Wandelt eine Alpha-Angabe in ein Byte (0–255) um. |
546
+ | `ParseColor(color)` | Parst eine CSS-Farbe in die interne 32-Bit-Darstellung `0xAARRGGBB`. |
547
+ | `FormatColor(color, alphaDecimals = 3)` | Serialisiert die interne Darstellung zurück zu CSS (`#rrggbb` bzw. `rgba(...)`). |
548
+ | `PxUnit(value)` | Zahlen werden zu `"<n>px"`, alles andere (z. B. `calc()`-Strings) wird unverändert durchgereicht. |
549
+
550
+ ## Regel- und Dokument-Methoden
551
+
552
+ Innerhalb von Direktiven (und Helper-Funktionen mit `this`-Bindung) stehen zusätzlich die Manipulations-Methoden der umgebenden Regel bereit:
553
+
554
+ | Methode / Eigenschaft | Beschreibung |
555
+ | :--- | :--- |
556
+ | `AddProperty(name, value, important?)` | Hängt eine Deklaration an die Regel an (Duplikate erlaubt — für Fallback-Ketten wie mehrfache `background-image`). |
557
+ | `ChangeProperty(name, value, important?)` | Ändert alle Deklarationen der Property bzw. legt sie an, wenn sie fehlt. |
558
+ | `RemoveProperty(name)` | Entfernt alle Deklarationen der Property. |
559
+ | `AddRule(selector)` | Fügt eine neue Regel am Dokumentende an; Rückgabe ist die Regel (mit denselben Methoden). |
560
+ | `InsertRule(selector)` | Fügt eine neue Regel direkt hinter der aktuellen Regel ein; aufeinanderfolgende Aufrufe behalten ihre Reihenfolge. Ersatz für das alte `AddBlock(n, µ.elementNo)`. |
561
+ | `rule` | Die umgebende Regel als `CssRule`-Objekt (`rule.selector`, `rule.GetProperty(...)`, …). |
562
+ | `document` | Das gesamte Stylesheet als `CssDocument` — z. B. für Pfad-Adressierung: `document.FindRule("@keyframes glittery", "from")`. Ersatz für die alten RememberBlocks. |
563
+
564
+ ## Sprite()
565
+
566
+ Registriert das Bild der Regel für den Sprite-Atlas (nur als Direktive):
567
+
568
+ ```css
569
+ -µ: Sprite(url, optionen?);
570
+ ```
571
+
572
+ | Parameter / Option | Default | Beschreibung |
573
+ | :--- | :--- | :--- |
574
+ | `url` | — (Pflicht) | Bild-URL relativ zum Skin-Verzeichnis. |
575
+ | `offsetWidth` | `0` | Wird zur berechneten `width` addiert. |
576
+ | `offsetHeight` | `0` | Wird zur berechneten `height` addiert. |
577
+ | `offsetPosX` | `0` | Wird zur `background-position`-X-Koordinate addiert. |
578
+ | `offsetPosY` | `0` | Wird zur `background-position`-Y-Koordinate addiert. |
579
+ | `afterWork` | — | Hook-Funktion, läuft nach der Atlas-Auflösung (siehe unten). |
580
+
581
+ Beim Atlas-Auflösen werden `background-image` (`url(...)` plus `image-set(...)` bei Retina), `background-repeat`, `background-position`, `width` und `height` der Regel gesetzt; vorhandene Deklarationen dieser Properties werden ersetzt.
582
+
583
+ ## Cursor()
584
+
585
+ Wendet eine Cursor-Definition aus dem Manifest an — als Direktive (`-µ: Cursor("zoom");`, schreibt die `cursor`-Deklarationen der Regel um) oder als Wertform (`cursor: µ(Cursor("zoom"));`, liefert nur den `url(...) x y, fallback`-String). Unbekannte Namen werden unverändert durchgereicht — Standard-Cursor wie `pointer` funktionieren so ohne Definition.
586
+
587
+ ## Helper-Funktionen und this-Bindung
588
+
589
+ Helper aus dem Manifest, die mit `function` deklariert sind, werden mit `this` = Auswertungs-Scope aufgerufen. Damit lassen sich Makros im Stil der alten `µ.$.Funktionen` schreiben — nur als normales, testbares JavaScript:
590
+
591
+ ```js
592
+ // helpers.mjs
593
+ import { Lighten } from "gulp-mu-css";
594
+
595
+ export function Borders(_baseColor, _pixelWidth, _topLighten, _rightLighten, _bottomLighten, _leftLighten) {
596
+ this.AddProperty("border-top", `${_pixelWidth}px solid ${Lighten(_baseColor, _topLighten)}`);
597
+ this.AddProperty("border-right", `${_pixelWidth}px solid ${Lighten(_baseColor, _rightLighten)}`);
598
+ this.AddProperty("border-bottom", `${_pixelWidth}px solid ${Lighten(_baseColor, _bottomLighten)}`);
599
+ this.AddProperty("border-left", `${_pixelWidth}px solid ${Lighten(_baseColor, _leftLighten)}`);
600
+ }
601
+ ```
602
+
603
+ Über `this` sind alle Scope-Bindungen erreichbar: `this.AddProperty(...)`, `this.InsertRule(...)`, `this.rule`, `this.document` und `this.$`. Die Bindung bleibt auch erhalten, wenn ein Helper als `afterWork`-Wert an `Sprite()` übergeben wird. Arrow-Functions haben kein eigenes `this` und eignen sich daher nur für Helpers ohne Regel-Zugriff.
604
+
605
+ Portierte Referenz-Makros des AiDPix-Projekts (`Borders`, `TableBackgrounds`, `GlitterySprite`, `FlyEx`, `FlyExUtils`) liegen im µCSS-Repository unter `examples/aidpix-helpers.mjs`.
606
+
607
+ ## afterWork-Hooks
608
+
609
+ Der `afterWork`-Hook einer Sprite-Registrierung läuft, nachdem der Atlas gepackt und die Regel umgeschrieben wurde. Er erhält einen Kontext mit allen Ergebnisdaten:
610
+
611
+ | Feld | Beschreibung |
612
+ | :--- | :--- |
613
+ | `rule` | Die umgeschriebene Regel (`CssRule`). |
614
+ | `document` | Das Stylesheet (`CssDocument`). |
615
+ | `url` | Die registrierte Bild-URL. |
616
+ | `baseDir` | Skin-Verzeichnis (zum Auflösen von Nachbar-Dateien, z. B. `.json`-Maps). |
617
+ | `sprite` | `{ x, y, width, height }` — Position und Größe im Atlas. |
618
+ | `atlas` | `{ file, retinaFile, width, height }` — die Atlas-Dateien und -Gesamtgröße. |
619
+
620
+ Typischer Einsatz: Animations-Keyframes aus den Frame-Daten eines `sequenceStrip` generieren (siehe `GlitterySprite` in den Referenz-Makros).
621
+
622
+ # Arbeiten mit Farben
623
+
624
+ ## Farben definieren
625
+
626
+ Die interne Darstellung einer Farbe ist ein vorzeichenloser 32-Bit-Integer der Form `0xAARRGGBB` (Alpha, Rot, Grün, Blau; je 8 Bit). In allen Farb-Funktionen sind folgende Eingabeformen möglich:
627
+
628
+ - **32-Bit-Integer** wie die interne Darstellung, z. B. `0xffff0000` für deckendes Rot.
629
+ - **`#`-Notation** mit zwei Hex-Ziffern pro Kanal, z. B. `"#00ff00"` (Alpha wird auf deckend gesetzt).
630
+ - **Kurze `#`-Notation** mit einer Hex-Ziffer pro Kanal, z. B. `"#0f0"`.
631
+ - **Erweiterte `#`-Notation** mit Alpha-Kanal als vierte Komponente, z. B. `"#0000ff80"` für halbtransparentes Blau.
632
+ - **`rgb()`-Notation**, absolut oder prozentual, z. B. `"rgb(255,0,0)"` oder `"rgb(100%,0%,0%)"`.
633
+ - **`rgba()`-Notation** mit Float-Alpha, z. B. `"rgba(255,0,0,0.5)"`.
634
+ - **CSS-Farbnamen** (W3C CSS Color Module, erweiterte Liste), z. B. `"red"`, `"teal"`, `"rebeccapurple"` sowie `"transparent"`.
635
+
636
+ Werte außerhalb des gültigen Bereichs werden begrenzt (geclippt).
637
+
638
+ ## Alpha-Werte
639
+
640
+ Alpha-Angaben (z. B. für `Alpha(color, a)`) sind in mehreren Formen möglich:
641
+
642
+ - **Float** zwischen `0.0` (transparent) und `1.0` (deckend).
643
+ - **Integer** zwischen `0` und `255`.
644
+ - **Hex-String**, z. B. `"0x80"`.
645
+ - **Prozent-String** von `"0%"` bis `"100%"`.
646
+ - **Schlüsselwörter** `"transparent"` (0), `"translucent"` (128) und `"opaque"` (255).
647
+
648
+ ## Farbberechnungen
649
+
650
+ Die Funktionen `Lighten`, `Alpha` und `MixColors` (Kapitel „µ-Auswertungskontext") decken die typischen Berechnungen ab. Deckende Ergebnisse werden als `#rrggbb` ausgegeben, transparente als `rgba(r,g,b,a)` mit drei Alpha-Nachkommastellen — identisch zum alten µCSS.
651
+
652
+ ```css
653
+ div.menu {
654
+ background-color: µ(Alpha($.baseBrgdColor, 0.9));
655
+ border-top: 1px solid µ(Lighten($.baseBrgdColor, 0.3));
656
+ border-bottom: 1px solid µ(Lighten($.baseBrgdColor, -0.3));
657
+ }
658
+ ```
659
+
660
+ Für neue Skins empfiehlt sich das `"oklch"`-Modell von `Lighten`: Es verändert die wahrgenommene Helligkeit gleichmäßig über alle Farbtöne, während das Legacy-Modell `"hsl"` bei gesättigten Farben sichtbar springen kann.
661
+
662
+ # Node-API
663
+
664
+ Neben dem Manifest-Workflow ist jede Schicht von µCSS auch direkt als Node-API nutzbar — etwa für eigene Gulp-Transformationen oder Tests.
665
+
666
+ | Export | Beschreibung |
667
+ | :--- | :--- |
668
+ | `CompileMcss(source, options)` | Kompiliert `.µ.css`-Quelltext zu einem `CssDocument`. Optionen: `vars`, `helpers`, `from` (Dateiname für Fehlermeldungen), `context` (gemeinsamer `MuContext`), `sprites` (SpriteManager), `cursors` (CursorManager). |
669
+ | `CssDocument` / `CssRule` | JSON-fähiger Wrapper über den PostCSS-AST: `FromFile`/`FromString`, `FindRule(...pfad)`, `FindRules(selektorOderRegex)`, `AddRule`, `GetProperty`/`AddProperty`/`ChangeProperty`/`RemoveProperty`, `ToCss()`, `ToFile()`, `ToJson()`/`FromJson()`. Für Spezialfälle ist der rohe PostCSS-AST über `document.root` zugänglich. |
670
+ | `SpriteManager` | Sprite-Registrierung und Atlas-Auflösung: `new SpriteManager({ baseDir, atlasFile, retina, padding, preloadRule, preload, writeMapFile })`, `Register(rule, url, optionen)`, `await Resolve(document)`. |
671
+ | `CursorManager` | Cursor-Definitionen: `new CursorManager(definitionen, { baseDir, preload })`, `Apply(rule, name)`, `Value(name)`. |
672
+ | `PreloadRegistry` | Sammelt Bild-URLs und erzeugt die `div.csspreload`-Regel. |
673
+ | `DefineSkin(config)` / `BuildSkin(manifest, options)` | Manifest-Deklaration und Skin-Build (siehe Kapitel „Build-Ablauf"). |
674
+ | `MuContext` | Auswertungskontext für eigene Pipelines: `new MuContext({ vars, helpers })`, `Evaluate(quelltext, extraScope)`. |
675
+ | `Lighten`, `Alpha`, `AlphaValue`, `MixColors`, `ParseColor`, `FormatColor`, `PxUnit` | Die Farb- und Wert-Funktionen als direkte Importe. |
676
+
677
+ Beispiel einer JSON-basierten Gulp-Manipulation:
678
+
679
+ ```js
680
+ import { CssDocument } from "gulp-mu-css";
681
+
682
+ const doc = await CssDocument.FromFile("skins/src/src.µ.css");
683
+ doc.FindRules(/^div\.panel/).forEach((_rule) => _rule.AddProperty("outline", "none"));
684
+ doc.FindRule("@keyframes glittery", "from").ChangeProperty("background-position-x", "-128px");
685
+ const json = doc.ToJson();
686
+ await doc.ToFile("out/std.css");
687
+ ```
688
+
689
+ # Fehlerdiagnostik
690
+
691
+ Build-Fehler nennen immer den Verursacher samt Quellbezug:
692
+
693
+ - **Inline-JavaScript** (`µ(...)`-Interpolationen und `-µ:`-Direktiven): Fehler werden als PostCSS-Fehler mit Datei, Zeile, Spalte und Quelltext-Ausschnitt gemeldet. Bei mehreren `µ(...)` in einem Wert wird der fehlschlagende Ausdruck genannt. JavaScript-Syntaxfehler erscheinen als `invalid JavaScript expression "<Quelltext>" (...)`.
694
+
695
+ ```
696
+ CssSyntaxError: skins/src/std.µ.css:2:2: microCSS: µ(NopeFn(1)): NopeFn is not defined
697
+
698
+ 1 | div.b {
699
+ > 2 | border: 1px solid µ(NopeFn(1));
700
+ | ^
701
+ 3 | }
702
+ ```
703
+
704
+ - **Fehlende Sprite-Bilder**: Vor dem Packen des Atlas wird die Existenz aller Quellbilder (inklusive `@2x` bei `retina: true`) geprüft. Alle fehlenden Dateien werden gesammelt in einem Fehler gemeldet — jeweils mit URL, aufgelöstem Pfad und der referenzierenden Regel:
705
+
706
+ ```
707
+ SpriteManager: 2 sprite image(s) not found:
708
+ - "imgs/nope.png" -> C:\...\skins\std\imgs\nope.png - selector "div.bad1" (skin.µ.css:2)
709
+ - "imgs/alsonope.png" -> C:\...\skins\std\imgs\alsonope.png - selector "div.bad2" (skin.µ.css:3)
710
+ ```
711
+
712
+ - **`afterWork`-Hooks**: Fehler im Hook werden mit Sprite-URL und Regel-Quellposition ummantelt; der Original-Fehler bleibt als `cause` erhalten.
713
+ - **Fehlende Cursor-Bilder** erzeugen nur eine Warnung (einmal pro Cursor), da die CSS über den Fallback-Cursor funktionsfähig bleibt.
714
+ - **media-Steps**: Fehler nennen Step-Nummer und -Typ, z. B. `BuildSkin: media step 3 of 7 (buttonsAndIcons: "dev/media/buttons.psd") failed: ...`. Fehlende Quellen werden vor der Ausführung geprüft: `copy`/`copyFolder` und Generator-Steps melden den aufgelösten Pfad statt eines rohen Dateisystemfehlers — bei `copyFolder` mit dem Hinweis, dass vermutlich der erzeugende Schritt (z. B. Sequenzbild-Generierung nach `dev/media/final/...`) nicht gelaufen ist.
715
+ - **files-Einträge**: Fehlende Quelldateien werden mit Eintrag und aufgelöstem Pfad gemeldet, bevor kompiliert wird.
716
+
717
+ # Migration von µCSS
718
+
719
+ Für Bestandsprojekte existiert ein Konverter-Werkzeug (`tools/convert-mucss.mjs` im µCSS-Repository), das die alte Syntax mechanisch übersetzt:
720
+
721
+ | Alt (µCSS 1) | Neu (µCSS 2) |
722
+ | :--- | :--- |
723
+ | `-µcss: µ.Cursor("wait");` plus `cursor: wait;` | `cursor: µ(Cursor("wait"));` |
724
+ | `-µcss: µ.SetBackgroundColor(µ.$.x);` plus Platzhalter | `background-color: µ($.x);` |
725
+ | `-µcss: µ.AddProperty("border", "1px solid " + µ.Lighten(...));` | `border: 1px solid µ(Lighten(...));` |
726
+ | `-µcss: µ.Sprite("p.png");` | `-µ: Sprite("p.png");` |
727
+ | `-µcss: µ.SetRememberBlock("n");` | entfällt — Pfad-Adressierung: `document.FindRule("@keyframes x", "from")` |
728
+ | `-µcss: //µ.X(...)` (deaktiviert) | `/* -µ: X(...) */` |
729
+ | `µ.$.name = wert` (in `µ.std.css`) | `vars`-Eintrag im Manifest |
730
+ | `µ.DefCursor(...)` | `cursors`-Eintrag im Manifest |
731
+ | `µ.plugins.*` / FileCopy | `media`-Einträge im Manifest |
732
+ | `µ.$.Fn = function(...)«…»` | exportierte Funktion in `helpers.mjs` |
733
+
734
+ Die `«»¡`-Funktionskörper werden zu normalem JavaScript zurückübersetzt und landen als Startpunkt in `helpers.mjs`; manuelle Nacharbeit ist hier eingeplant.
735
+
736
+ **raw → final automatisch:** Das alte µCSS kopierte nur die bereits fertig erzeugten `final`-Ordner in den Skin (z. B. `flyex`, `glittery`); die Strips selbst entstanden außerhalb (SpriteTools). Der Konverter erkennt solche `CopyFolder2Skin`-Aufrufe auf `dev/media/final/<…>/<name>` und stellt — falls eine passende Quelle `dev/media/raw/<name>/imgs` existiert — automatisch den passenden `sequenceStrip`-Step mit `outputBase: "project"` davor: eine **nummerierte PNG-Frame-Sequenz** (z. B. `glittery`) wird zu einem Ordner-Strip, **einzeln benannte Bilder** (z. B. die DSD-Bilder `flyex.png`/`flyexutils.png`) je zu einem DSD-Strip. So baut die neue Pipeline `final` reproduzierbar aus `raw` und der nachfolgende `copyFolder` übernimmt das Ergebnis in den Skin.
737
+
738
+ Der Konverter wurde am vollständigen AiDPix-Bestand validiert: Ein Vergleichswerkzeug (`tools/compare-aidpix.mjs`) baut den konvertierten Skin und vergleicht ihn regel- und property-weise gegen die alte kompilierte Ausgabe — Ergebnis: 2 951 Regeln, 0 unerwartete Differenzen (53 dokumentierte Drift-Fälle, z. B. nach dem letzten µCSS-Lauf editierte Quellen).
739
+
740
+ Bewusst **nicht** übernommen wurden aus dem alten µCSS:
741
+
742
+ - **Vendor-Prefixes** (`-webkit-`/`-moz-`/`-ms-`-Duplikate): Alle genutzten Features sind seit Jahren unprefixt verfügbar. Vendor-spezifische Selektoren ohne Standard-Pendant (z. B. `::-webkit-scrollbar`) werden natürlich unverändert durchgereicht.
743
+ - **Photoshop-Dialogfenster** im Build: Interaktive Parameter gehören in das Manifest oder in Umgebungsvariablen.
744
+ - **FTP-Synchronisation**: Dafür gibt es heute etablierte Werkzeuge (CI/CD, rsync, gulp-Plugins).
745
+ - **Die Ersatzzeichen `«»¡`**: siehe Kapitel „Grundideen".
746
+
747
+ # Versionshistorie
748
+
749
+ | Datum | Version | Anmerkungen |
750
+ | :--- | :--- | :--- |
751
+ | 2013 | 1.0 | Ursprüngliches µCSS als Adobe-Photoshop-Script: `-µcss:`-Direktiven, Sprite-Atlas, PSD-Plugins, Steuerung über `µ.std.css`. |
752
+ | 2026-06 | 2.0.0 | Vollständige Node.js-Neuimplementierung (npm-Paket `gulp-mu-css`, ohne Adobe-Abhängigkeit): Core-Pipeline (M1), Sprites & Cursor (M2), Manifest & Build mit inkrementellem Cache (M3), Hooks & Makros (M4), AiDPix-Migration mit Konverter und Abnahmetest (M5), Handbuch (M6). |
753
+
754
+ # Rechtliches
755
+
756
+ Diese Software wird unter der MIT-Lizenz veröffentlicht und ist für freie und kommerzielle Nutzung freigegeben. Weder Dongleware noch der Autor haften für Schäden, die durch die Nutzung dieser Software entstehen. Die Nutzung erfolgt auf eigenes Risiko; sichern Sie Ihre Arbeit, bevor Sie die Werkzeuge einsetzen.
757
+
758
+ Autor: Meinolf Amekudzi.
759
+
760
+ ## MIT-Lizenz (Originaltext)
761
+
762
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
763
+
764
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
765
+
766
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
767
+
768
+ ## Marken
769
+
770
+ Photoshop ist eine eingetragene Marke von Adobe Inc.; Affinity Photo ist eine Marke von Serif (Europe) Ltd. Die Nennung von Namen und Produkten dient ausschließlich der Information und stellt keinen Missbrauch der jeweiligen Handelsnamen oder Marken dar.
771
+
772
+ ## Verwendete Bibliotheken
773
+
774
+ µCSS und µPS nutzen Open-Source-Bibliotheken Dritter, insbesondere PostCSS (CSS-Parser, MIT-Lizenz), sharp (Bildverarbeitung, Apache-2.0-Lizenz) und ag-psd (PSD-Leser, MIT-Lizenz). Der Bin-Packer des Sprite-Atlas basiert auf node-bin-packing (©2011 Jake Gordon und Mitwirkende, MIT-Lizenz).