@maccesar/titools 2.10.0 → 3.1.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.
Files changed (99) hide show
  1. package/AGENTS-VERCEL-RESEARCH.md +23 -12
  2. package/README.md +81 -182
  3. package/agents/ti-pro.md +8 -14
  4. package/lib/cleanup.js +13 -0
  5. package/lib/commands/skills.js +2 -1
  6. package/lib/commands/update.js +4 -1
  7. package/lib/config.js +9 -11
  8. package/lib/platform.js +1 -1
  9. package/package.json +2 -3
  10. package/skills/purgetss/SKILL.md +12 -0
  11. package/skills/purgetss/references/animation-advanced.md +0 -9
  12. package/skills/purgetss/references/app-branding.md +20 -0
  13. package/skills/purgetss/references/appearance-module.md +85 -27
  14. package/skills/purgetss/references/apply-directive.md +60 -0
  15. package/skills/purgetss/references/arbitrary-values.md +41 -0
  16. package/skills/purgetss/references/cli-commands.md +52 -17
  17. package/skills/purgetss/references/custom-rules.md +36 -19
  18. package/skills/purgetss/references/customization-deep-dive.md +37 -15
  19. package/skills/purgetss/references/grid-layout.md +45 -26
  20. package/skills/purgetss/references/icon-fonts.md +27 -25
  21. package/skills/purgetss/references/installation-setup.md +28 -2
  22. package/skills/purgetss/references/ios-large-titles.md +31 -15
  23. package/skills/purgetss/references/multi-density-images.md +57 -0
  24. package/skills/purgetss/references/opacity-modifier.md +11 -2
  25. package/skills/purgetss/references/semantic-colors.md +44 -1
  26. package/skills/purgetss/references/tikit-components.md +1 -3
  27. package/skills/alloy-guides/SKILL.md +0 -190
  28. package/skills/alloy-guides/references/CLI_TASKS.md +0 -233
  29. package/skills/alloy-guides/references/CONCEPTS.md +0 -171
  30. package/skills/alloy-guides/references/CONTROLLERS.md +0 -279
  31. package/skills/alloy-guides/references/MODELS.md +0 -1214
  32. package/skills/alloy-guides/references/PURGETSS.md +0 -46
  33. package/skills/alloy-guides/references/VIEWS_DYNAMIC.md +0 -235
  34. package/skills/alloy-guides/references/VIEWS_STYLES.md +0 -375
  35. package/skills/alloy-guides/references/VIEWS_WITHOUT_CONTROLLERS.md +0 -102
  36. package/skills/alloy-guides/references/VIEWS_XML.md +0 -581
  37. package/skills/alloy-guides/references/WIDGETS.md +0 -160
  38. package/skills/alloy-howtos/SKILL.md +0 -181
  39. package/skills/alloy-howtos/references/best_practices.md +0 -121
  40. package/skills/alloy-howtos/references/cli_reference.md +0 -230
  41. package/skills/alloy-howtos/references/config_files.md +0 -158
  42. package/skills/alloy-howtos/references/custom_tags.md +0 -148
  43. package/skills/alloy-howtos/references/debugging_troubleshooting.md +0 -78
  44. package/skills/alloy-howtos/references/samples.md +0 -156
  45. package/skills/ti-api/SKILL.md +0 -109
  46. package/skills/ti-api/references/api-android.md +0 -675
  47. package/skills/ti-api/references/api-app-platform.md +0 -636
  48. package/skills/ti-api/references/api-core.md +0 -764
  49. package/skills/ti-api/references/api-data-network.md +0 -641
  50. package/skills/ti-api/references/api-media.md +0 -655
  51. package/skills/ti-api/references/api-modules-ble-bluetooth.md +0 -657
  52. package/skills/ti-api/references/api-modules-coremotion-urlsession.md +0 -411
  53. package/skills/ti-api/references/api-modules-map.md +0 -632
  54. package/skills/ti-api/references/api-modules-nfc.md +0 -725
  55. package/skills/ti-api/references/api-modules-social-misc.md +0 -526
  56. package/skills/ti-api/references/api-services.md +0 -700
  57. package/skills/ti-api/references/api-ui-android.md +0 -499
  58. package/skills/ti-api/references/api-ui-extras.md +0 -702
  59. package/skills/ti-api/references/api-ui-ios-animator.md +0 -378
  60. package/skills/ti-api/references/api-ui-ios.md +0 -756
  61. package/skills/ti-api/references/api-ui-lists.md +0 -581
  62. package/skills/ti-api/references/api-ui-text-input.md +0 -607
  63. package/skills/ti-api/references/api-ui-views.md +0 -572
  64. package/skills/ti-api/references/api-ui-windows-navigation.md +0 -676
  65. package/skills/ti-api/references/api-xml-global.md +0 -743
  66. package/skills/ti-guides/SKILL.md +0 -75
  67. package/skills/ti-guides/references/advanced-data-and-images.md +0 -155
  68. package/skills/ti-guides/references/android-manifest.md +0 -97
  69. package/skills/ti-guides/references/app-distribution.md +0 -373
  70. package/skills/ti-guides/references/application-frameworks.md +0 -366
  71. package/skills/ti-guides/references/cli-reference.md +0 -700
  72. package/skills/ti-guides/references/coding-best-practices.md +0 -150
  73. package/skills/ti-guides/references/commonjs-advanced.md +0 -279
  74. package/skills/ti-guides/references/hello-world.md +0 -99
  75. package/skills/ti-guides/references/hyperloop-native-access.md +0 -458
  76. package/skills/ti-guides/references/javascript-primer.md +0 -402
  77. package/skills/ti-guides/references/reserved-words.md +0 -36
  78. package/skills/ti-guides/references/resources.md +0 -172
  79. package/skills/ti-guides/references/style-and-conventions.md +0 -104
  80. package/skills/ti-guides/references/tiapp-config.md +0 -655
  81. package/skills/ti-howtos/SKILL.md +0 -143
  82. package/skills/ti-howtos/references/android-platform-deep-dives.md +0 -609
  83. package/skills/ti-howtos/references/automation-fastlane-appium.md +0 -96
  84. package/skills/ti-howtos/references/buffer-codec-streams.md +0 -162
  85. package/skills/ti-howtos/references/cross-platform-development.md +0 -358
  86. package/skills/ti-howtos/references/debugging-profiling.md +0 -473
  87. package/skills/ti-howtos/references/extending-titanium.md +0 -684
  88. package/skills/ti-howtos/references/google-maps-v2.md +0 -172
  89. package/skills/ti-howtos/references/ios-map-kit.md +0 -149
  90. package/skills/ti-howtos/references/ios-platform-deep-dives.md +0 -595
  91. package/skills/ti-howtos/references/local-data-sources.md +0 -310
  92. package/skills/ti-howtos/references/location-and-maps.md +0 -267
  93. package/skills/ti-howtos/references/media-apis.md +0 -268
  94. package/skills/ti-howtos/references/notification-services.md +0 -539
  95. package/skills/ti-howtos/references/remote-data-sources.md +0 -339
  96. package/skills/ti-howtos/references/tutorials.md +0 -552
  97. package/skills/ti-howtos/references/using-modules.md +0 -182
  98. package/skills/ti-howtos/references/web-content-integration.md +0 -288
  99. package/skills/ti-howtos/references/webpack-build-pipeline.md +0 -125
@@ -112,6 +112,18 @@ purgetss create 'MyApp' -d -v fa
112
112
  # -v: Copy icon fonts (fa, mi, ms, f7)
113
113
  ```
114
114
 
115
+ ## What's New in v7.9.0
116
+
117
+ - Opacity modifier on semantic colors — `bg-surface/65` syntax now works for any class mapped through `theme.extend.colors`. PurgeTSS auto-derives `<originalKey>_<alphaPercent>` entries in `semantic.colors.json` per mode (light/dark). See [Semantic Colors → Opacity modifier auto-derivation](references/semantic-colors.md#opacity-modifier-auto-derivation). **Native rebuild required** — Liveview hot-reload alone does not refresh `semantic.colors.json`
118
+ - `theme.Window` / `theme.View` / `theme.ImageView` presets now use **replace mode** — they no longer carry framework defaults (white background, `Ti.UI.SIZE`, iOS `hires: true`). This fixes gradient ghosting where a preset Window with `bg-gradient-*` apply was being covered by the implicit `backgroundColor: '#FFFFFF'`. Use `theme.extend.Window` / `theme.extend.View` / `theme.extend.ImageView` when you want **extend mode** (merge with defaults). See [Apply Directive → Extend mode vs replace mode](references/apply-directive.md#extend-mode-vs-replace-mode)
119
+ - Glossary path renamed: `purgetss/experimental/tailwind-classes/` → `purgetss/glossary/tailwind-classes/`
120
+
121
+ ## What's New in v7.8.0
122
+
123
+ - `purgetss images --width <n>` — pin SVG output to a specific base width; PurgeTSS derives per-density assets from the multiplier scale (×1 / ×1.5 / ×2 / ×3 / ×4). Range `[1, 8192]`. CLI-only (no `images:` config equivalent — width is per-asset). See [Multi-Density Images → Pinning the output width with --width](references/multi-density-images.md#pinning-the-output-width-with---width)
124
+ - Class syntax pre-validation — the build now emits a `Class Syntax Error` block (file path + line + `Fix:` suggestion) for 5 detected patterns: inverted negative sign (`top-(-10)` → `-top-(10)`), Tailwind brackets (`top-[10px]` → `top-(10px)`), empty parens (`wh-()`), whitespace inside parens (`wh-( 200 )` → `wh-(200)`), redundant `px` unit (`top-(10px)` → `top-(10)`). See [Arbitrary Values → Class syntax pre-validation](references/arbitrary-values.md#class-syntax-pre-validation)
125
+ - Parser fix for negative values inside parentheses — `top-(-10)` is now correctly flagged as inverted-negative-sign instead of silently misparsed
126
+
115
127
  ## What's New in v7.7.0
116
128
 
117
129
  - `brand:` config restructured — flat keys replaced by purpose-based groups: `brand.logos`, `brand.padding`, `brand.android`, `brand.ios`, `brand.colors`
@@ -238,15 +238,6 @@ const { saveComponent } = require('purgetss.ui')
238
238
  saveComponent({ source: $.myView, directory: 'screenshots' })
239
239
  ```
240
240
 
241
- ### createAnimation(args)
242
-
243
- Factory function to create Animation objects programmatically.
244
-
245
- ```javascript
246
- const { createAnimation } = require('purgetss.ui')
247
- const anim = createAnimation({ duration: 300, curve: Ti.UI.ANIMATION_CURVE_EASE_IN_OUT })
248
- ```
249
-
250
241
  ---
251
242
 
252
243
  ## Implementation Rules
@@ -301,6 +301,16 @@ brand: {
301
301
  >
302
302
  > Generating `splash_icon.png` does not automatically switch Titanium to use it for the Android 12+ system splash. Titanium still needs a custom splash theme that points `android:windowSplashScreenAnimatedIcon` to `@drawable/splash_icon`. If you do nothing, Android keeps using `ic_launcher`.
303
303
 
304
+ > **WARNING**
305
+ >
306
+ > Merge splash settings into the existing Android theme
307
+ > If your app already has a custom Android theme block in `tiapp.xml`, **merge** the new splash settings (the `android:windowSplashScreenAnimatedIcon` entry and any sibling theme attributes) into that existing theme. Do **not** append a second `<application>` element or a duplicate theme block — Titanium will only honor one theme definition, and the duplicate silently shadows or overrides the original, leading to "my settings were ignored" debugging sessions. Always edit the existing `<application>` / theme block in place.
308
+
309
+ > **INFO**
310
+ >
311
+ > A brief flash on splash exit is usually the system, not your PNGs
312
+ > If you still see a brief flash or abrupt exit transition during splash dismissal even with correct assets in place, **do not assume the PNGs PurgeTSS generates are wrong**. That artifact commonly comes from Android 12+'s system splash exit transition (or from Titanium's splash theme handoff to your first window), not from the splash icon files themselves. Regenerating `splash_icon.png` will not change it — the fix lives in the splash theme animation, not in the icon assets.
313
+
304
314
  ## Android legacy splash fallback
305
315
 
306
316
  Since v7.7.0, PurgeTSS regenerates `app/assets/android/default.png` in Alloy projects and `Resources/android/default.png` in Classic projects.
@@ -411,6 +421,16 @@ purgetss brand --cleanup-legacy --aggressive
411
421
  > Commit first
412
422
  > `--cleanup-legacy` deletes files permanently. Commit your project to git before running without `--dry-run` so `git restore` is available as a rollback.
413
423
 
424
+ > **INFO**
425
+ >
426
+ > Files kept on purpose
427
+ > `--cleanup-legacy` intentionally does **not** remove the following files, even when modern adaptive launcher icons and splash assets are already in place:
428
+ >
429
+ > - `app/assets/android/default.png` (Alloy projects)
430
+ > - `Resources/android/default.png` (Classic projects)
431
+ >
432
+ > These remain because they are still required by Titanium framework defaults — older Android splash code paths fall back to `default.png`, and removing it can cause build warnings or a missing-asset error on certain Titanium SDK versions even when modern splash assets exist. Treat them as part of the baseline asset set, not as legacy clutter.
433
+
414
434
  ## Troubleshooting
415
435
 
416
436
  ### The icon looks cropped or cramped on my phone
@@ -2,15 +2,71 @@
2
2
 
3
3
  The `Appearance` export from `purgetss.ui` (added in **v7.5.3**) handles Light / Dark / System mode switching and persists the user's choice across app restarts. It is a thin, deterministic wrapper around Titanium's native `Ti.UI.overrideUserInterfaceStyle` plus `Ti.App.Properties`, exposed as a singleton so every controller in the app reads and writes the same state.
4
4
 
5
- This file covers the four public methods, the required setup, a full Settings-view example, and the lifecycle that keeps the saved mode in sync with the UI. For the color wiring that actually makes the UI *respond* to mode changes, see [semantic-colors.md](./semantic-colors.md).
5
+ This reference follows the same workflow-first / API-second split the official PurgeTSS documentation now uses:
6
+
7
+ - **Workflow** — initialize at startup, then build a Settings view that calls `Appearance.set(...)` (mirrors `best-practices/1-appearance-setup.md`).
8
+ - **API reference** — the four public methods, exact signatures, and lifecycle (mirrors `purgetss-ui/10-appearance.md`).
9
+
10
+ For the color wiring that actually makes the UI *respond* to mode changes, see [semantic-colors.md](./semantic-colors.md).
6
11
 
7
12
  > **INFO**
8
13
  >
9
14
  > Requires PurgeTSS **>= 7.5.3**. On older versions the `Appearance` export does not exist — the `require('purgetss.ui')` call will succeed, but `Appearance` will be `undefined`. Run `purgetss --version` in the project root to confirm.
10
15
 
11
- ## Setup
16
+ ## How it fits together
17
+
18
+ The Appearance module sits between three moving parts: a JS singleton, the OS-level interface style, and the semantic colors generated by PurgeTSS. The diagram below shows how a single `Appearance.init()` call at startup wires them together, and what happens when the user later picks a mode in Settings.
19
+
20
+ ```
21
+ ┌─ app/lib/appearance.js (singleton from purgetss.ui) ─────────────────┐
22
+ │ │
23
+ │ Appearance.init() ────► reads 'userInterfaceStyle' │
24
+ │ from Ti.App.Properties │
25
+ │ applies Ti.UI.overrideUserInterfaceStyle │
26
+ │ │
27
+ │ Appearance.set(m) ────► writes Ti.App.Properties │
28
+ │ applies Ti.UI.overrideUserInterfaceStyle │
29
+ │ │
30
+ │ Appearance.get() ────► returns 'system' | 'light' | 'dark' │
31
+ │ Appearance.toggle()────► flips between 'light' and 'dark' │
32
+ │ │
33
+ └──────────────────────────────────────────────────────────────────────┘
34
+
35
+
36
+ ┌─ Ti.UI.semanticColorType (runtime) ──────────────────────────────────┐
37
+ │ │
38
+ │ Light mode ─► resolves bg-surface to surfaceColor.light │
39
+ │ resolves text-on-surface to textColor.light │
40
+ │ │
41
+ │ Dark mode ─► resolves bg-surface to surfaceColor.dark │
42
+ │ resolves text-on-surface to textColor.dark │
43
+ │ │
44
+ └──────────────────────────────────────────────────────────────────────┘
45
+
46
+
47
+ ┌─ Utility classes in views ───────────────────────────────────────────┐
48
+ │ │
49
+ │ bg-surface, text-on-surface, bg-border, bg-surface-high, ... │
50
+ │ Repaint instantly on every Appearance.set(...) call. │
51
+ │ │
52
+ └──────────────────────────────────────────────────────────────────────┘
53
+ ```
54
+
55
+ `Ti.App.Properties` is the persistence layer — the value written during `set(...)` is what `init()` reads on the next launch. Clearing app storage or reinstalling resets to `'system'`.
56
+
57
+ ## Workflow
58
+
59
+ The fastest path from zero to a working Light / Dark toggle is three steps. Each one maps to one section of the official `best-practices/1-appearance-setup.md` guide.
12
60
 
13
- Call `Appearance.init()` **once** at app startup, before opening the first window. The standard place is the top of `app/controllers/index.js`.
61
+ ### 1. Define semantic colors
62
+
63
+ Create `app/assets/semantic.colors.json` and register the color names under `theme.extend.colors` in `purgetss/config.cjs`. The class names you use in XML (`bg-surface`, `text-on-surface`, `bg-border`, ...) come from this mapping.
64
+
65
+ See [semantic-colors.md](./semantic-colors.md) for the JSON schema, the `config.cjs` mapping, nesting rules, and alpha transparency.
66
+
67
+ ### 2. Initialize Appearance at startup
68
+
69
+ Call `Appearance.init()` **once** before opening the first window. The standard place is the top of `app/controllers/index.js`:
14
70
 
15
71
  `app/controllers/index.js`
16
72
  ```js
@@ -27,22 +83,11 @@ $.navWin.open()
27
83
  >
28
84
  > Call `Appearance.init()` **before** `$.navWin.open()` (or whichever window you open first). If the window opens first, it will render once against the system default and then flicker to the saved mode when `init()` runs — a visible flash on cold launch.
29
85
 
30
- ## Methods
31
-
32
- | Method | Description |
33
- | ------------ | --------------------------------------------------------------------------- |
34
- | `init()` | Restore the saved mode from `Ti.App.Properties` and apply it via `Ti.UI.overrideUserInterfaceStyle`. Call once at app startup before opening the first window. |
35
- | `set(mode)` | Apply and persist a mode. Accepts `'system'`, `'light'`, or `'dark'`. Any other value is silently ignored. |
36
- | `get()` | Returns the current mode as a string: `'system'`, `'light'`, or `'dark'`. |
37
- | `toggle()` | Switch between `'light'` and `'dark'`. Skips `'system'` — if current mode is `'system'` the next call produces `'dark'`. |
38
-
39
- All four methods are **synchronous**. They do not fire events; if you need to react to a mode change, call your update logic right after `Appearance.set(...)`.
40
-
41
- ## Full Settings-view example
86
+ ### 3. Build an Appearance toggle
42
87
 
43
- A three-row settings panel — System / Light / Dark — where each row shows a `fa-solid fa-circle-check` icon next to the currently active mode. Tapping a row calls `Appearance.set(...)` and updates the check icons.
88
+ Build a Settings view where users pick `System`, `Light`, or `Dark`. Tapping a row calls `Appearance.set(...)` and updates the check icons.
44
89
 
45
- ### View
90
+ #### View
46
91
 
47
92
  `app/views/settings.xml`
48
93
  ```xml
@@ -86,7 +131,7 @@ A three-row settings panel — System / Light / Dark — where each row shows a
86
131
  </Alloy>
87
132
  ```
88
133
 
89
- ### Controller
134
+ #### Controller
90
135
 
91
136
  `app/controllers/settings.js`
92
137
  ```js
@@ -95,16 +140,16 @@ const { Appearance } = require('purgetss.ui')
95
140
  // Sync check icons with the mode saved at app startup.
96
141
  updateUI(Appearance.get())
97
142
 
98
- const selectDark = () => selectAppearance('dark')
99
- const selectLight = () => selectAppearance('light')
100
- const selectSystem = () => selectAppearance('system')
143
+ function selectDark() { selectAppearance('dark') }
144
+ function selectLight() { selectAppearance('light') }
145
+ function selectSystem() { selectAppearance('system') }
101
146
 
102
- const selectAppearance = (value) => {
147
+ function selectAppearance(value) {
103
148
  Appearance.set(value)
104
149
  updateUI(value)
105
150
  }
106
151
 
107
- const updateUI = (value) => {
152
+ function updateUI(value) {
108
153
  $.themeDarkCheck.visible = (value === 'dark')
109
154
  $.themeLightCheck.visible = (value === 'light')
110
155
  $.themeSystemCheck.visible = (value === 'system')
@@ -113,9 +158,24 @@ const updateUI = (value) => {
113
158
 
114
159
  The background, text, border, and icon colors in the XML come from semantic color classes (`bg-surface`, `bg-surface-high`, `text-on-surface`, `bg-border`, etc.) — see [semantic-colors.md](./semantic-colors.md) for how to wire those up so the whole view flips when `Appearance.set('dark')` fires.
115
160
 
116
- ## Lifecycle
161
+ > **Why named functions in Alloy controllers?** Alloy resolves `onClick="selectDark"` by looking up a top-level identifier on the controller scope. Named function declarations (`function selectDark() { ... }`) hoist and are reliably resolvable; arrow functions assigned to `const` are not hoisted and tend to bite you when an XML attribute fires before the binding has executed. The official setup guide uses named functions for this reason.
117
162
 
118
- How the four moving parts — `Appearance.init()`, `Ti.App.Properties`, `Ti.UI.overrideUserInterfaceStyle`, and semantic colors — cooperate:
163
+ ## API reference
164
+
165
+ ### Methods
166
+
167
+ | Method | Description |
168
+ | ------------ | --------------------------------------------------------------------------- |
169
+ | `init()` | Restore the saved mode from `Ti.App.Properties` and apply it via `Ti.UI.overrideUserInterfaceStyle`. Call once at app startup before opening the first window. |
170
+ | `set(mode)` | Apply and persist a mode. Accepts `'system'`, `'light'`, or `'dark'`. Any other value is silently ignored. |
171
+ | `get()` | Returns the current mode as a string: `'system'`, `'light'`, or `'dark'`. |
172
+ | `toggle()` | Switch between `'light'` and `'dark'`. Skips `'system'` — if current mode is `'system'` the next call produces `'dark'`. |
173
+
174
+ All four methods are **synchronous**. They do not fire events; if you need to react to a mode change, call your update logic right after `Appearance.set(...)`.
175
+
176
+ ### Lifecycle
177
+
178
+ How the four moving parts — `Appearance.init()`, `Ti.App.Properties`, `Ti.UI.overrideUserInterfaceStyle`, and semantic colors — cooperate during a typical session:
119
179
 
120
180
  ```
121
181
  app startup
@@ -143,8 +203,6 @@ app startup
143
203
  +-- Preference survives the next cold launch
144
204
  ```
145
205
 
146
- `Ti.App.Properties` is the persistence layer — the value written during `set(...)` is what `init()` reads on the next launch. Clearing app storage or reinstalling resets to `'system'`.
147
-
148
206
  > **INFO**
149
207
  >
150
208
  > `toggle()` intentionally skips `'system'`. If the current mode is `'system'`, calling `toggle()` produces `'dark'`. If you want a three-way cycle (System -> Light -> Dark -> System), build it in userland with `Appearance.get()` + `Appearance.set(...)`; do not expect `toggle()` to do it.
@@ -254,6 +254,66 @@ Window: { default: { apply: 'exit-on-close-false bg-blue-500' } }
254
254
 
255
255
  Use the explicit `default:` wrapper when you also need platform blocks (`ios:`, `android:`) next to it. For the common case of one bundle of defaults, the shorthand reads better.
256
256
 
257
+ ### Extend mode vs replace mode
258
+
259
+ > **HEADLINE CHANGE — v7.9.0**
260
+ > This is the headline change of PurgeTSS v7.9.0. Before v7.9.0, `theme.Window` / `theme.View` / `theme.ImageView` still had the framework defaults merged in, which caused gradient ghosting and other surprising overrides. v7.9.0 makes top-level (non-`extend`) configs behave as true replace mode.
261
+ >
262
+ > If you previously used `theme.Window` (no `extend`) and depended on the white background or the iOS `hires: true` / `Ti.UI.SIZE` defaults still being there, you will need to either move that config under `theme.extend.Window` (extend mode) or add the previously-implicit utilities back into your `apply` string.
263
+
264
+ PurgeTSS follows the Tailwind convention for the three Ti Elements that ship with framework defaults — `Window`, `View`, and `ImageView`:
265
+
266
+ - **Extend mode** — `theme.extend.Window`, `theme.extend.View`, `theme.extend.ImageView`. Your customization **merges** with the framework defaults. The white Window `backgroundColor`, `Ti.UI.SIZE` width/height on `View`, and iOS `hires: true` on `ImageView` stay in place unless you override them with `apply`. Use this when you want to add to the defaults, not replace them.
267
+ - **Replace mode** — `theme.Window`, `theme.View`, `theme.ImageView` (top level, no `extend`). Your config **replaces** the framework defaults entirely. The white Window background is omitted, the `Ti.UI.SIZE` width/height on `View` is omitted, and the iOS `hires: true` on `ImageView` is omitted. Your `apply` becomes the source of truth for that Ti Element.
268
+
269
+ Use replace mode when you want full control and do not want a preset mixed in. The canonical case is a Window declared at `theme.Window` with a `backgroundGradient`, where the previously-merged white `backgroundColor` would render on top of the gradient and produce a "ghost" white wash.
270
+
271
+ `./purgetss/config.cjs - replace mode (v7.9.0+)`
272
+ ```javascript
273
+ module.exports = {
274
+ theme: {
275
+ Window: {
276
+ apply: 'bg-gradient-to-b from-blue-500 to-purple-600'
277
+ }
278
+ }
279
+ };
280
+ ```
281
+
282
+ `./purgetss/styles/utilities.tss`
283
+ ```tss
284
+ 'Window': { backgroundGradient: { type: 'linear', colors: [...], startPoint: ..., endPoint: ... } }
285
+ ```
286
+
287
+ Note the missing `backgroundColor: '#FFFFFF'`. Replace mode skipped the framework default, so the gradient renders cleanly. Before v7.9.0 the same config produced:
288
+
289
+ ```tss
290
+ /* Pre-v7.9.0 — ghost white background covering the gradient */
291
+ 'Window': { backgroundColor: '#FFFFFF', backgroundGradient: { type: 'linear', colors: [...] } }
292
+ ```
293
+
294
+ If you wanted the framework white background **and** something else on top, that is what extend mode is for:
295
+
296
+ `./purgetss/config.cjs - extend mode`
297
+ ```javascript
298
+ module.exports = {
299
+ theme: {
300
+ extend: {
301
+ Window: {
302
+ apply: 'exit-on-close-false'
303
+ }
304
+ }
305
+ }
306
+ };
307
+ ```
308
+
309
+ `./purgetss/styles/utilities.tss`
310
+ ```tss
311
+ /* Framework default backgroundColor is preserved, exitOnClose merged in */
312
+ 'Window': { backgroundColor: '#FFFFFF', exitOnClose: false }
313
+ ```
314
+
315
+ The same extend-vs-replace distinction applies to `View` (`Ti.UI.SIZE` width/height defaults) and `ImageView` (iOS `hires: true` default).
316
+
257
317
  ### Apply Wins Over Static Defaults
258
318
 
259
319
  If `apply` sets a property that the component already has as a built-in default, the applied value replaces the original instead of both ending up in the final TSS:
@@ -7,6 +7,47 @@ When you need a one-off value that is not in the defaults, use arbitrary values
7
7
  >
8
8
  > You cannot use square bracket notation like in Tailwind because Titanium handles platform and conditional statements in `.tss` files differently.
9
9
 
10
+ ## Class syntax pre-validation
11
+
12
+ Starting with PurgeTSS v7.8.0, the build runs a pre-validation pass over class names found in XML views and JS controllers before purging. When it spots a malformed arbitrary-value utility, it stops and prints a structured `Class Syntax Error` block so the offending class can be fixed before any TSS is generated. If multiple errors are present, all of them are reported in the same run.
13
+
14
+ Each error block includes:
15
+
16
+ - The file path where the offending class lives
17
+ - The line number inside that file
18
+ - The offending class name (as authored)
19
+ - A `Fix:` suggestion with the corrected class name
20
+
21
+ Example shape:
22
+
23
+ ```
24
+ Class Syntax Error
25
+ File: app/views/index.xml
26
+ Line: 12
27
+ Found: top-(-10)
28
+ Fix: -top-(10)
29
+ ```
30
+
31
+ ### Detected patterns
32
+
33
+ The validator catches five narrow, actionable mistakes:
34
+
35
+ | Pattern | Offending input | Suggested fix | Notes |
36
+ | ----------------------------- | --------------- | ------------- | ---------------------------------------------------------------- |
37
+ | Inverted negative sign | `top-(-10)` | `-top-(10)` | The `-` prefix goes before the rule, not inside the value |
38
+ | Tailwind-style brackets | `top-[10px]` | `top-(10px)` | PurgeTSS uses parentheses, not square brackets, for arbitrary values |
39
+ | Empty parentheses | `wh-()` | (flagged, no auto-fix) | Add a value such as `wh-(10)` |
40
+ | Whitespace inside parentheses | `wh-( 200 )` | `wh-(200)` | No spaces allowed between `(`, the value, and `)` |
41
+ | Redundant `px` unit | `top-(10px)` | `top-(10)` | PurgeTSS treats unit-less arbitrary values as pixels |
42
+
43
+ ### Unknown classes are still silently dropped
44
+
45
+ The pre-validator only fires on the five patterns above. Any other unknown class — typos, custom utilities not yet declared, vendor classes that are not enabled in `config.cjs` — is **not** reported as a `Class Syntax Error`. Those classes continue to flow into the `// Unused or unsupported classes` comment block in `app.tss`, exactly like before. This keeps the validator focused on actionable mistakes and avoids noise while you sketch out class names.
46
+
47
+ ### v7.8.0 parser fix for negatives inside parentheses
48
+
49
+ Before v7.8.0, an inverted negative such as `top-(-10)` could be silently misparsed by the arbitrary-value pipeline (the `-` inside the parentheses confused the token matcher, producing wrong or missing TSS output without any warning). v7.8.0 hardens that path: the parser now correctly recognizes `top-(-10)` as authored, classifies it as an inverted-negative-sign error, and surfaces the `Class Syntax Error` block with the `-top-(10)` fix instead of producing silent garbage.
50
+
10
51
  ## Color Properties
11
52
 
12
53
  You can set arbitrary color values for all available color properties using `hex`, `rgb`, or `rgba` values, directly in XML files or in `config.cjs`.
@@ -1,6 +1,12 @@
1
1
  # PurgeTSS CLI Commands
2
2
 
3
- > **Info: What's new in v7.5.3 / v7.6.x / v7.7.0**
3
+ > **Info: What's new in v7.5.3 / v7.6.x / v7.7.0 / v7.8.0 / v7.9.0**
4
+ > - **Opacity modifiers on semantic colors (v7.9.0)** — writing `bg-surface/65` (or any opacity modifier on a class mapped to a name in `semantic.colors.json`) now produces a working rule with Light/Dark switching preserved. PurgeTSS auto-derives a `<originalKey>_<alphaPercent>` entry in `semantic.colors.json` with the original `light`/`dark` hex values plus the requested alpha for both modes, then emits the rule against the derived key. Re-runs are idempotent; manual edits with conflicting values halt the build with a `Conflict` error. New alpha entries require one full Titanium build to be picked up — Liveview hot-reload alone does not refresh `semantic.colors.json`.
5
+ > - **Gradient + Window/View/ImageView preset fixes (v7.9.0)** — `theme.Window` / `theme.View` / `theme.ImageView` no longer leak the framework presets (white background, `Ti.UI.SIZE`, iOS `hires: true`) when defined at the top level (replace mode), so a Window declared at `theme.Window` with a `backgroundGradient` no longer ghosts on top of a default `backgroundColor: '#FFFFFF'`. `theme.extend.Window` keeps merging with the defaults as before. Also fixed: tonal palette inversion bug, position-dependent `from`/`to` color ordering after `sort()`, and `bg-gradient-to-X` direction silently dropped when combined with `from-X to-Y` colors.
6
+ > - **Glossary path renamed (v7.9.0, breaking)** — the user-facing glossary output path was renamed from `purgetss/experimental/tailwind-classes/` to `purgetss/glossary/tailwind-classes/`. Tooling or CI that reads from the old path needs updating on upgrade — no transition shim. The `--glossary` flag and command surface are unchanged.
7
+ > - **`images --width <n>` flag (v7.8.0)** — pins Android `mdpi` (= iPhone `@1x`) to a specific pixel width, with larger scales deriving as ×1.5, ×2, ×3, ×4 from that base. Use it for SVG sources from vector editors with disproportionate viewBoxes (Affinity, Illustrator). CLI-only; there is no matching `images:` config property because the right width is per-asset. See [`images` Command](#images-command).
8
+ > - **Class syntax pre-validation (v7.8.0)** — `purgetss` now stops with a structured `Class Syntax Error` block (file + line + suggested fix) when it detects known class-name mistakes: inverted negative sign (`top-(-10)` → `-top-(10)`), Tailwind-style brackets (`top-[10px]` → `top-(10px)`), empty parentheses (`wh-()`), whitespace inside parentheses (`wh-( 200 )`), and redundant `px` unit (`top-(10px)` → `top-(10)`). All offenders are reported in one run. Generic unknown classes still flow into the `// Unused or unsupported classes` block in `app.tss`.
9
+ > - **Negative-values-in-parens parser fix (v7.8.0)** — classes like `top-(-10)`, `mt-(-5)`, and `origin-(-10,-20)` no longer crash with `Cannot read properties of null (reading 'pop')`. The parser now extracts the `(...)` portion first, so a `-` inside the value does not break the split.
4
10
  > - **`brand:` config grouped (v7.7.0)** — the flat `brand:` block from v7.6.0 was reorganized into purpose-based sections: `brand.logos`, `brand.padding`, `brand.android`, `brand.ios`, and `brand.colors`. Old projects keep working — newly-generated configs use the grouped form.
5
11
  > - **Separate Android brand inputs (v7.7.0)** — `brand` can now use one logo for the general brand set, another for Android launcher icons (`logos.androidLauncher` / `--icon-logo`), and another for Android 12+ splash artwork (`logos.androidSplash` / `--splash-logo`). Drop `logo-icon.*` and `logo-splash.*` into `purgetss/brand/` or set the paths in `config.cjs`.
6
12
  > - **Legacy Android splash fallback (v7.7.0)** — `purgetss brand` now regenerates `app/assets/android/default.png` (Alloy) or `Resources/android/default.png` (Classic). `cleanup-legacy` no longer removes `default.png`.
@@ -91,15 +97,14 @@ module.exports = {
91
97
  splash: false, // also generate splash_icon.png × 5
92
98
  notification: false // also generate ic_stat_notify.png × 5
93
99
  },
100
+ ios: {
101
+ dark: true, // emit DefaultIcon-Dark.png (set false to skip)
102
+ tinted: true, // emit DefaultIcon-Tinted.png (set false to skip)
103
+ darkBackground: null // null = transparent per Apple HIG; set a hex like '#111111' for an opaque dark bg
104
+ },
94
105
  colors: {
95
106
  background: '#FFFFFF' // Android adaptive bg + iOS/marketplace flatten
96
107
  },
97
- // Optional iOS overrides:
98
- // ios: {
99
- // dark: false,
100
- // tinted: false,
101
- // darkBackground: '#111111'
102
- // },
103
108
  confirmOverwrites: true // prompt before overwriting files (set false to skip)
104
109
  },
105
110
  images: {
@@ -283,15 +288,14 @@ brand: {
283
288
  splash: false, // also generate splash_icon.png × 5
284
289
  notification: false // also generate ic_stat_notify.png × 5
285
290
  },
291
+ ios: {
292
+ dark: true, // emit DefaultIcon-Dark.png (set false to skip)
293
+ tinted: true, // emit DefaultIcon-Tinted.png (set false to skip)
294
+ darkBackground: null // null = transparent per Apple HIG; set a hex like '#111' for an opaque dark bg
295
+ },
286
296
  colors: {
287
297
  background: '#FFFFFF' // Android adaptive bg + iOS/marketplace flatten
288
298
  },
289
- // Optional iOS overrides:
290
- // ios: {
291
- // dark: false, // skip DefaultIcon-Dark.png
292
- // tinted: false, // skip DefaultIcon-Tinted.png
293
- // darkBackground: '#111' // opaque dark bg for DefaultIcon-Dark.png
294
- // },
295
299
  confirmOverwrites: true // prompt before overwriting files
296
300
  }
297
301
  ```
@@ -344,6 +348,7 @@ purgetss images background/ # re-process one subfolder
344
348
  | `--ios` | Only emit iPhone scale variants. Mutually exclusive with `--android`. |
345
349
  | `--format <ext>` | Convert all outputs to `webp`, `jpeg`, `png`, `avif`, `gif`, or `tiff`. Default: keep source format. |
346
350
  | `--quality <n>` | Quality `0-100` for lossy formats. Default `85`. |
351
+ | `--width <n>` | (v7.8.0) Pin Android `mdpi` (= iPhone `@1x`) to `<n>` pixels wide. Larger scales derive as ×1.5, ×2, ×3, ×4 from that base; height stays proportional to the source's aspect ratio. Integer in `[1, 8192]`. |
347
352
  | `--dry-run` | Preview without writing any files. |
348
353
  | `--project <path>` | Project root (defaults to cwd). |
349
354
  | `-y, --yes` | Skip the overwrite confirmation prompt. |
@@ -353,6 +358,12 @@ purgetss images background/ # re-process one subfolder
353
358
 
354
359
  - `[source]` (optional) — path to override auto-discovery. Resolves first against `purgetss/images/` (short paths like `buttons/btn.png`), then against cwd.
355
360
 
361
+ ### When to use `--width`
362
+
363
+ Use `--width <n>` for SVG sources from vector editors with disproportionate viewBoxes — common in Affinity, Illustrator, and other design tools where the viewBox does not match the intended display size. Without the flag, every scale derives from the source's natural pixel size as a 4× master, which can produce unexpected output sizes.
364
+
365
+ When you pass an SVG without `--width`, the command prints a one-time hint and falls back to the legacy 4× behavior. This is CLI-only; there is no matching `images:` config property because the right width is per-asset.
366
+
356
367
  ### Config block
357
368
 
358
369
  Defaults live under `images:` in `purgetss/config.cjs`:
@@ -375,6 +386,7 @@ purgetss images background/pink-texture.png # re-process one file (sh
375
386
  purgetss images background/ # re-process one subfolder
376
387
  purgetss images --android # only Android densities
377
388
  purgetss images --format webp --quality 90 # convert all outputs to WebP
389
+ purgetss images logo.svg --width 256 # pin SVG output to 256 px @1x/mdpi
378
390
  purgetss images --dry-run # preview
379
391
  ```
380
392
 
@@ -412,6 +424,29 @@ purgetss semantic --single '#000000' overlayColor --alpha 50
412
424
 
413
425
  When `--dark` is omitted, it defaults to the light hex — useful for overlays/glass surfaces where alpha is the only variation.
414
426
 
427
+ ### Customizing the class name
428
+
429
+ The auto-mapping uses the most literal Titanium-style transform: strip `Color`, then kebab-case the rest (e.g. `surfaceColor` → `surface`, `textSecondaryColor` → `text-secondary`). If your design system prefers different names — for example `on-surface` instead of `text`, or nesting the surface family under `DEFAULT` / `high` — edit `config.cjs` after running the `--single` batch:
430
+
431
+ `./purgetss/config.cjs`
432
+ ```javascript
433
+ theme: {
434
+ extend: {
435
+ colors: {
436
+ surface: { DEFAULT: 'surfaceColor', high: 'surfaceHighColor' },
437
+ 'on-surface': 'textColor',
438
+ 'on-surface-variant': 'textSecondaryColor',
439
+ muted: 'textMutedColor',
440
+ border: 'borderColor',
441
+ accent: 'accentColor',
442
+ overlay: 'overlayColor'
443
+ }
444
+ }
445
+ }
446
+ ```
447
+
448
+ The next `purgetss build` picks up the renamed classes. Editing one generated mapping is faster than typing the whole structure from scratch. See [semantic-colors.md](./semantic-colors.md) for the full nested-vs-flat discussion (including the `[object Object]` pitfall when nesting without `DEFAULT`).
449
+
415
450
  ### Smart in-place updates
416
451
 
417
452
  If a `--single` name matches an existing palette shade — e.g. `purgetss semantic --single '#000' amazon500` while palette `amazon` exists — PurgeTSS narrows the operation to an in-place JSON value edit. The entry stays in its original position, and `config.cjs` is left untouched (the palette already maps to that key).
@@ -482,10 +517,10 @@ After copying the fonts, you can use them in Buttons and Labels. For example, fo
482
517
 
483
518
  ### Available Font Classes
484
519
 
485
- - [fontawesome.tss](https://github.com/macCesar/purgeTSS/blob/master/dist/fontawesome.tss)
486
- - [materialicons.tss](https://github.com/macCesar/purgeTSS/blob/master/dist/materialicons.tss)
487
- - [materialsymbols.tss](https://github.com/macCesar/purgeTSS/blob/master/dist/materialsymbols.tss)
488
- - [framework7icons.tss](https://github.com/macCesar/purgeTSS/blob/master/dist/framework7icons.tss)
520
+ - [fontawesome.tss](https://github.com/macCesar/purgeTSS/blob/main/dist/fontawesome.tss)
521
+ - [materialicons.tss](https://github.com/macCesar/purgeTSS/blob/main/dist/materialicons.tss)
522
+ - [materialsymbols.tss](https://github.com/macCesar/purgeTSS/blob/main/dist/materialsymbols.tss)
523
+ - [framework7icons.tss](https://github.com/macCesar/purgeTSS/blob/main/dist/framework7icons.tss)
489
524
 
490
525
  ### Copying Specific Font Vendors
491
526
 
@@ -1,35 +1,49 @@
1
1
  # Custom Rules
2
2
 
3
- Custom rules in PurgeTSS let you style Titanium elements, IDs, and classes in `config.cjs`. You can also target specific platforms, devices, or conditional blocks using global variables. Handy when a project spans iOS and Android and you want to keep styles in one place.
3
+ Custom rules in PurgeTSS let you style Titanium elements, IDs, and classes from `config.cjs`. They are the bridge between the utility-first classes you write in XML and the platform-, device-, or condition-specific overrides Titanium projects often need. Because everything lives in a single `config.cjs`, projects that span iOS and Android can keep one source of truth for styling instead of fragmenting rules across multiple `.tss` files.
4
+
5
+ This file mirrors the official rewrite — section names, examples, and ordering match the upstream docs so cross-referencing stays cheap.
4
6
 
5
7
  ## Classes, IDs, and Ti Elements
6
8
 
7
- Whether you want to style a Ti Element (also known as a markup element), a custom ID prefixed with a hash (`#`), or a custom class prefixed with a period (`.`), the structure is the same.
9
+ Whether you want to style a Ti Element (a markup element such as `Label`), a custom ID prefixed with `#`, or a custom class prefixed with `.`, the structure inside `config.cjs` is the same. You always declare a key (the selector) whose value is an object containing one or more block keys (`DEFAULT`, `ios`, `android`, `tablet`, `handheld`, or `[if=...]`).
8
10
 
9
11
  ### Modifier Key
10
12
 
11
- - For Titanium elements, use the exact name of the element, such as `Label`, `Button`, or `ScrollView`.
12
- - For IDs, use `camelCase` to match the JavaScript convention.
13
- - For classes, use `kebab-case` to stay compatible with PurgeTSS v6.x and above. For example, use `.my-custom-class-name` instead of `.myCustomClassName`.
14
-
15
- > **CAUTION -- PurgeTSS v5 or Earlier Projects**
16
- > If your project started on PurgeTSS v5 or earlier and you now use 7.x.x or later, set `purge.options.missing` to `true` in `config.cjs`. It will report missing classes at the end of `app.tss` so you can update them to the new naming convention.
13
+ - For Titanium elements, use the **exact name** of the element, such as `Label`, `Button`, or `ScrollView`. The casing must match Titanium's API (e.g. `TextField`, not `textfield` or `Text Field`).
14
+ - For IDs, use `camelCase` to match Alloy's JavaScript convention (e.g. `mainBanner` referenced in XML as `id="mainBanner"`). The selector key inside `config.cjs` includes the `#` prefix.
15
+ - For classes, use `kebab-case` to stay compatible with PurgeTSS v6.x and above. Use `.my-custom-class-name`, not `.myCustomClassName`. The kebab-case requirement is what most legacy projects trip over when they upgrade.
16
+
17
+ > **CAUTION Migrating from PurgeTSS v4 or v5 to v7+**
18
+ >
19
+ > If your project started on PurgeTSS v5 or earlier and you are now on 7.x.x or later, the class-naming convention changed (`camelCase` -> `kebab-case`) and the config moved to ESM-style `config.cjs`. The recommended upgrade path is:
20
+ >
21
+ > 1. **Audit current usage.** Set `purge.options.missing` to `true` in `config.cjs`. PurgeTSS will append a list of missing classes at the end of `app.tss` after the next compile — every entry is a class name you still need to migrate.
22
+ > 2. **Rename selectors.** Convert every `.myCustomClassName` to `.my-custom-class-name` in both XML markup and `config.cjs`. The old camelCase names will not match anymore.
23
+ > 3. **Convert config to `.cjs`.** If your project still uses `purgetss.config.js` or a CommonJS-style file with the old layout, rename the file to `config.cjs` and ensure it sits in `./purgetss/`. The single export must be `module.exports = { theme: { ... } }`.
24
+ > 4. **Re-run `purgetss build`.** Verify the missing-classes list at the end of `app.tss` is empty before turning `missing` back off.
25
+ >
26
+ > Skipping any of these steps tends to surface as silently dropped styles at runtime — the app compiles, but selectors no longer match.
17
27
 
18
28
  ### Default, Platform, Device, or Conditional Blocks
19
29
 
20
- - To generate a global style, use either the lowercase `default` or the uppercase `DEFAULT` keyword.
21
- - To target a specific platform, use the `ios` or `android` keywords.
22
- - To target a specific device, use the `tablet` or `handheld` keywords.
23
- - To target a condition with a global variable, use the `[if=globalVariableName]` keyword.
30
+ Inside any selector you can declare one or more of the following block keys. They translate to TSS query suffixes at compile time:
31
+
32
+ - **Global style**: use either the lowercase `default` or the uppercase `DEFAULT` keyword. This becomes the bare selector with no platform/device qualifier.
33
+ - **Specific platform**: use `ios` or `android`. These compile to `[platform=ios]` and `[platform=android]` query suffixes.
34
+ - **Specific device**: use `tablet` or `handheld`. These compile to `[formFactor=tablet]` and `[formFactor=handheld]`.
35
+ - **Conditional via global variable**: use `[if=globalVariableName]`. The exact key is preserved in the generated TSS, so you can reference any `Alloy.Globals.*` boolean.
24
36
 
25
37
  ### Property Values
26
38
 
27
- - For `Titanium` constants, `Alloy Configuration Values`, or `Global Variables`, always enclose them in quotes.
28
- - For `color` values, you can use `hex`, `8-digit hex`, `rgb(R,G,B)`, `rgba(R,G,B,A)`, `transparent`, or any of the standard color names. Use hex values if you want to avoid issues with the opacity modifier.
29
- - For `spacing` values, you can use different types of units: `em`, `rem`, `%`, `px`, `dp`, `cm`, or `in`.
30
- - `%`, `px`, `cm`, or `in` are passed through without conversion.
31
- - `em` or `rem` values are converted with this formula: `value * 16`.
32
- - `dp` removes the unit and keeps the value as-is.
39
+ The way you write property values determines how PurgeTSS emits them in the generated TSS. Pay particular attention to quoting — it is the single most common source of confusion.
40
+
41
+ - For `Titanium` constants, `Alloy` configuration values, or global variables, **always enclose them in quotes** in `config.cjs` (e.g. `'Ti.UI.SIZE'`, `'Alloy.CFG.iPhoneXNotchSize'`). The quotes are stripped in the generated TSS so the constant resolves at runtime — without them, JavaScript would try to evaluate the constant inside `config.cjs`, which fails because Titanium's globals are not available there.
42
+ - For `color` values, you can use `hex`, `8-digit hex` (with alpha), `rgb(R,G,B)`, `rgba(R,G,B,A)`, `transparent`, or any standard color name. Prefer hex values when you plan to use opacity modifiers — `rgba()` and named colors interact awkwardly with opacity utilities.
43
+ - For `spacing` values you can mix unit types. Each unit follows a specific conversion rule:
44
+ - `%`, `px`, `cm`, and `in` are passed through to TSS without conversion.
45
+ - `em` and `rem` values are converted with the formula `value * 16` (so `1rem` becomes `16`).
46
+ - `dp` removes the unit and keeps the numeric value as-is, since Titanium treats unit-less numeric values as density-independent pixels by default.
33
47
 
34
48
  ## `config.cjs` File Example
35
49
 
@@ -81,7 +95,10 @@ module.exports = {
81
95
  };
82
96
  ```
83
97
 
84
- `./purgetss/styles/utilities.tss`
98
+ ## Custom `./purgetss/styles/utilities.tss` File
99
+
100
+ PurgeTSS reads `config.cjs` and emits a single `utilities.tss` file that Alloy then merges with the rest of the project's styling. The file below shows what the example above generates — note how block keys translate into TSS query suffixes (`[platform=android]`, `[formFactor=tablet]`, `[if=...]`).
101
+
85
102
  ```tss
86
103
  /* Property: TextField */
87
104
  /* Description: A single line text field. */