vtex-css-sanitizer-cli 1.0.3 → 1.0.5

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,75 @@
1
+ # Workflow: Build & Release
2
+ # Se dispara cuando se hace push de un tag con formato v*.*.*
3
+ # Compila la GUI para Windows y Linux y crea un GitHub Release con los binarios.
4
+
5
+ name: Build & Release
6
+
7
+ on:
8
+ push:
9
+ tags:
10
+ - 'v*.*.*'
11
+
12
+ permissions:
13
+ contents: write
14
+
15
+ jobs:
16
+ build:
17
+ strategy:
18
+ matrix:
19
+ include:
20
+ - os: ubuntu-latest
21
+ platform: linux
22
+ build_script: build:linux
23
+ - os: windows-latest
24
+ platform: win
25
+ build_script: build:win
26
+
27
+ runs-on: ${{ matrix.os }}
28
+
29
+ steps:
30
+ - name: Checkout
31
+ uses: actions/checkout@v4
32
+
33
+ - name: Setup Node.js
34
+ uses: actions/setup-node@v4
35
+ with:
36
+ node-version: 20
37
+
38
+ - name: Install dependencies
39
+ working-directory: gui
40
+ run: yarn install --frozen-lockfile
41
+
42
+ - name: Build & Package
43
+ working-directory: gui
44
+ run: yarn ${{ matrix.build_script }}
45
+
46
+ - name: Upload artifacts
47
+ uses: actions/upload-artifact@v4
48
+ with:
49
+ name: release-${{ matrix.platform }}
50
+ path: |
51
+ gui/release/*.exe
52
+ gui/release/*.AppImage
53
+ if-no-files-found: warn
54
+
55
+ release:
56
+ needs: build
57
+ runs-on: ubuntu-latest
58
+
59
+ steps:
60
+ - name: Download all artifacts
61
+ uses: actions/download-artifact@v4
62
+ with:
63
+ path: artifacts
64
+ merge-multiple: true
65
+
66
+ - name: List artifacts
67
+ run: find artifacts -type f | head -20
68
+
69
+ - name: Create GitHub Release
70
+ uses: softprops/action-gh-release@v2
71
+ with:
72
+ generate_release_notes: true
73
+ files: |
74
+ artifacts/**/*.exe
75
+ artifacts/**/*.AppImage
@@ -67,10 +67,9 @@ async function fixCommand(projectPath) {
67
67
  if (response.shouldDelete === undefined) {
68
68
  console.log('\n🛑 Proceso de limpieza cancelado por el usuario.');
69
69
  userCancelled = true;
70
- break; // Sale del bucle de reglas para este archivo
70
+ break;
71
71
  }
72
72
  if (response.shouldDelete) {
73
- // Se opera sobre la regla, pero el reporte se guarda temporalmente
74
73
  fileDeletedRules.push({ rule: ruleAsString, filePath });
75
74
  rule.remove();
76
75
  rulesRemovedInFile++;
@@ -82,7 +81,6 @@ async function fixCommand(projectPath) {
82
81
  }
83
82
  console.log('------------------------------------------------------------------');
84
83
  }
85
- // Si el usuario canceló, se detiene todo el proceso.
86
84
  if (userCancelled) {
87
85
  if (deletedRules.length > 0 || keptRules.length > 0) {
88
86
  await (0, report_generator_1.generateFixReport)(projectPath, deletedRules, keptRules);
@@ -90,7 +88,6 @@ async function fixCommand(projectPath) {
90
88
  }
91
89
  return;
92
90
  }
93
- // Si se completó el archivo y hubo cambios, se escriben en el disco.
94
91
  if (rulesRemovedInFile > 0) {
95
92
  const newContent = root.toString();
96
93
  await promises_1.default.writeFile(filePath, newContent, 'utf-8');
@@ -98,7 +95,6 @@ async function fixCommand(projectPath) {
98
95
  totalRulesRemoved += rulesRemovedInFile;
99
96
  await (0, prompts_1.default)({ type: 'invisible', name: 'continue', message: 'Presiona Enter para continuar con el siguiente archivo...' });
100
97
  }
101
- // Solo si el archivo se procesó por completo, se añaden las acciones al reporte final.
102
98
  deletedRules.push(...fileDeletedRules);
103
99
  keptRules.push(...fileKeptRules);
104
100
  }
@@ -7,12 +7,18 @@ exports.findJsonFiles = findJsonFiles;
7
7
  exports.findCssFiles = findCssFiles;
8
8
  const glob_1 = require("glob");
9
9
  const path_1 = __importDefault(require("path"));
10
+ /**
11
+ * Normaliza una ruta para que use forward slashes (/), requerido por glob en Windows.
12
+ */
13
+ function toGlobPath(p) {
14
+ return p.split(path_1.default.sep).join('/');
15
+ }
10
16
  /**
11
17
  * Encuentra todos los archivos .json y .jsonc en la carpeta /store.
12
18
  */
13
19
  function findJsonFiles(projectPath) {
14
20
  const storePath = path_1.default.join(projectPath, 'store');
15
- return (0, glob_1.sync)(`${storePath}/**/*.{json,jsonc}`);
21
+ return (0, glob_1.sync)(`${toGlobPath(storePath)}/**/*.{json,jsonc}`);
16
22
  }
17
23
  /**
18
24
  * Encuentra todos los archivos .css DE APPS NATIVAS (vtex.*) en la carpeta /styles/css.
@@ -20,7 +26,7 @@ function findJsonFiles(projectPath) {
20
26
  */
21
27
  function findCssFiles(projectPath) {
22
28
  const stylesPath = path_1.default.join(projectPath, 'styles/css');
23
- const allCssFiles = (0, glob_1.sync)(`${stylesPath}/**/*.css`);
29
+ const allCssFiles = (0, glob_1.sync)(`${toGlobPath(stylesPath)}/**/*.css`);
24
30
  const nativeVtexCssFiles = allCssFiles.filter(filePath => {
25
31
  const fileName = path_1.default.basename(filePath);
26
32
  return fileName.startsWith('vtex.');
Binary file
Binary file
@@ -0,0 +1,5 @@
1
+ <svg viewBox="0 0 24 24" fill="none" stroke="#6366f1" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M12 2L2 7l10 5 10-5-10-5z"/>
3
+ <path d="M2 17l10 5 10-5"/>
4
+ <path d="M2 12l10 5 10-5"/>
5
+ </svg>
@@ -0,0 +1,336 @@
1
+ <!DOCTYPE html>
2
+ <html lang="es">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>VTEX CSS Sanitizer — Limpiá tu CSS en VTEX IO</title>
8
+ <meta name="description"
9
+ content="Herramienta para detectar y eliminar CSS y blockClass no utilizados en proyectos VTEX IO. Disponible como CLI y aplicación de escritorio.">
10
+ <meta name="keywords" content="vtex, vtex io, css, sanitizer, cleanup, blockclass, developer tools">
11
+ <meta name="author" content="Elias Daniel Emanuele">
12
+
13
+ <!-- Open Graph -->
14
+ <meta property="og:title" content="VTEX CSS Sanitizer">
15
+ <meta property="og:description" content="Detectá y eliminá CSS y blockClass no utilizados en proyectos VTEX IO.">
16
+ <meta property="og:type" content="website">
17
+ <meta property="og:url" content="https://emanueleelias.github.io/vtex-css-sanitizer/">
18
+
19
+ <!-- Fonts -->
20
+ <link rel="preconnect" href="https://fonts.googleapis.com">
21
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
22
+ <link
23
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap"
24
+ rel="stylesheet">
25
+
26
+ <!-- Favicon -->
27
+ <link rel="icon" href="favicon.svg" type="image/svg+xml">
28
+
29
+ <link rel="stylesheet" href="styles.css">
30
+ </head>
31
+
32
+ <body>
33
+ <!-- ============ NAV ============ -->
34
+ <nav class="nav" id="nav">
35
+ <div class="nav__container">
36
+ <a href="#" class="nav__logo">
37
+ <svg class="nav__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
38
+ stroke-linecap="round" stroke-linejoin="round">
39
+ <path d="M12 2L2 7l10 5 10-5-10-5z" />
40
+ <path d="M2 17l10 5 10-5" />
41
+ <path d="M2 12l10 5 10-5" />
42
+ </svg>
43
+ <span>VTEX CSS Sanitizer</span>
44
+ </a>
45
+ <div class="nav__links">
46
+ <a href="#features">Features</a>
47
+ <a href="#versions">Versiones</a>
48
+ <a href="#downloads">Descargas</a>
49
+ <a href="https://github.com/emanueleelias/vtex-css-sanitizer" target="_blank" rel="noopener" class="nav__github"
50
+ aria-label="GitHub">
51
+ <svg viewBox="0 0 24 24" fill="currentColor" width="22" height="22">
52
+ <path
53
+ d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0024 12c0-6.63-5.37-12-12-12z" />
54
+ </svg>
55
+ </a>
56
+ </div>
57
+ </div>
58
+ </nav>
59
+
60
+ <!-- ============ HERO ============ -->
61
+ <header class="hero" id="hero">
62
+ <div class="hero__glow"></div>
63
+ <div class="container">
64
+ <div class="hero__badges">
65
+ <a href="https://www.npmjs.com/package/vtex-css-sanitizer-cli" target="_blank" rel="noopener" class="badge">
66
+ <span class="badge__dot badge__dot--npm"></span>npm v1.0.3
67
+ </a>
68
+ <a href="https://github.com/emanueleelias/vtex-css-sanitizer" target="_blank" rel="noopener" class="badge">
69
+ <span class="badge__dot badge__dot--gh"></span>Open Source
70
+ </a>
71
+ </div>
72
+ <h1 class="hero__title">
73
+ Limpiá el CSS<br>
74
+ de tus proyectos<br>
75
+ <span class="hero__highlight">VTEX IO</span>
76
+ </h1>
77
+ <p class="hero__subtitle">
78
+ Detectá reglas CSS huérfanas y <code>blockClass</code> sin uso.<br>
79
+ Mantené tu proyecto limpio, performante y fácil de mantener.
80
+ </p>
81
+ <div class="hero__actions">
82
+ <a href="#downloads" class="btn btn--primary">
83
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
84
+ stroke-linejoin="round" width="18" height="18">
85
+ <path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4" />
86
+ <polyline points="7 10 12 15 17 10" />
87
+ <line x1="12" y1="15" x2="12" y2="3" />
88
+ </svg>
89
+ Descargar
90
+ </a>
91
+ <a href="https://github.com/emanueleelias/vtex-css-sanitizer" target="_blank" rel="noopener"
92
+ class="btn btn--outline">
93
+ Ver en GitHub
94
+ </a>
95
+ </div>
96
+ </div>
97
+ </header>
98
+
99
+ <!-- ============ FEATURES ============ -->
100
+ <section class="features" id="features">
101
+ <div class="container">
102
+ <span class="section__label">Características</span>
103
+ <h2 class="section__title">Todo lo que necesitás</h2>
104
+ <div class="features__grid">
105
+ <article class="feature-card reveal">
106
+ <div class="feature-card__icon">
107
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
108
+ stroke-linejoin="round">
109
+ <circle cx="11" cy="11" r="8" />
110
+ <line x1="21" y1="21" x2="16.65" y2="16.65" />
111
+ </svg>
112
+ </div>
113
+ <h3>Análisis Bidireccional</h3>
114
+ <p>Encuentra CSS sin <code>blockClass</code> y <code>blockClass</code> sin CSS. Doble chequeo para no dejar
115
+ nada suelto.</p>
116
+ </article>
117
+ <article class="feature-card reveal">
118
+ <div class="feature-card__icon">
119
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
120
+ stroke-linejoin="round">
121
+ <path d="M12 20h9" />
122
+ <path d="M16.5 3.5a2.121 2.121 0 013 3L7 19l-4 1 1-4L16.5 3.5z" />
123
+ </svg>
124
+ </div>
125
+ <h3>Limpieza Interactiva</h3>
126
+ <p>El comando <code>fix</code> te guía regla por regla. Vos decidís qué se elimina y qué se conserva.</p>
127
+ </article>
128
+ <article class="feature-card reveal">
129
+ <div class="feature-card__icon">
130
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
131
+ stroke-linejoin="round">
132
+ <path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z" />
133
+ <polyline points="14 2 14 8 20 8" />
134
+ <line x1="16" y1="13" x2="8" y2="13" />
135
+ <line x1="16" y1="17" x2="8" y2="17" />
136
+ </svg>
137
+ </div>
138
+ <h3>Informes Detallados</h3>
139
+ <p>Genera reportes en Markdown con cada análisis y sesión de limpieza para un registro histórico.</p>
140
+ </article>
141
+ <article class="feature-card reveal">
142
+ <div class="feature-card__icon">
143
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
144
+ stroke-linejoin="round">
145
+ <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" />
146
+ </svg>
147
+ </div>
148
+ <h3>Seguro</h3>
149
+ <p>Ignora automáticamente los archivos CSS de componentes React custom para evitar falsos positivos.</p>
150
+ </article>
151
+ <article class="feature-card reveal">
152
+ <div class="feature-card__icon">
153
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
154
+ stroke-linejoin="round">
155
+ <polyline points="16 18 22 12 16 6" />
156
+ <polyline points="8 6 2 12 8 18" />
157
+ </svg>
158
+ </div>
159
+ <h3>Inteligente</h3>
160
+ <p>Reconoce clases de estado dinámicas de VTEX como <code>--isActive</code> y solo valida el
161
+ <code>blockClass</code> principal.</p>
162
+ </article>
163
+ <article class="feature-card reveal">
164
+ <div class="feature-card__icon">
165
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
166
+ stroke-linejoin="round">
167
+ <rect x="2" y="3" width="20" height="14" rx="2" ry="2" />
168
+ <line x1="8" y1="21" x2="16" y2="21" />
169
+ <line x1="12" y1="17" x2="12" y2="21" />
170
+ </svg>
171
+ </div>
172
+ <h3>CLI + GUI</h3>
173
+ <p>Usalo desde la terminal o con la aplicación de escritorio. Elegí la que mejor se adapte a tu workflow.</p>
174
+ </article>
175
+ </div>
176
+ </div>
177
+ </section>
178
+
179
+ <!-- ============ VERSIONS ============ -->
180
+ <section class="versions" id="versions">
181
+ <div class="container">
182
+ <span class="section__label">Versiones</span>
183
+ <h2 class="section__title">Dos formas de usarlo</h2>
184
+ <div class="versions__grid">
185
+ <!-- CLI -->
186
+ <div class="version-card reveal">
187
+ <div class="version-card__header">
188
+ <span class="version-card__tag">CLI</span>
189
+ <h3>Línea de Comandos</h3>
190
+ <p>Ideal para integrar en tu workflow de desarrollo.</p>
191
+ </div>
192
+ <div class="version-card__preview">
193
+ <img src="assets/cli-mockup.png" alt="VTEX CSS Sanitizer CLI en acción" loading="lazy">
194
+ </div>
195
+ <div class="version-card__install">
196
+ <div class="copy-block" id="copyBlock">
197
+ <code>npm install -g vtex-css-sanitizer-cli</code>
198
+ <button class="copy-block__btn" id="copyBtn" aria-label="Copiar comando">
199
+ <svg class="copy-block__icon copy-block__icon--copy" viewBox="0 0 24 24" fill="none"
200
+ stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
201
+ <rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
202
+ <path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1" />
203
+ </svg>
204
+ <svg class="copy-block__icon copy-block__icon--check" viewBox="0 0 24 24" fill="none"
205
+ stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
206
+ <polyline points="20 6 9 17 4 12" />
207
+ </svg>
208
+ </button>
209
+ </div>
210
+ </div>
211
+ </div>
212
+ <!-- GUI -->
213
+ <div class="version-card reveal">
214
+ <div class="version-card__header">
215
+ <span class="version-card__tag version-card__tag--gui">GUI</span>
216
+ <h3>Aplicación de Escritorio</h3>
217
+ <p>Interfaz visual intuitiva para analizar y limpiar tu proyecto sin tocar la terminal.</p>
218
+ </div>
219
+ <div class="version-card__preview">
220
+ <img src="assets/gui-mockup.png" alt="VTEX CSS Sanitizer GUI" loading="lazy">
221
+ </div>
222
+ <div class="version-card__platforms">
223
+ <span class="platform-chip">
224
+ <svg viewBox="0 0 24 24" fill="currentColor" width="16" height="16">
225
+ <path
226
+ d="M0 3.449L9.75 2.1v9.451H0m10.949-9.602L24 0v11.4H10.949M0 12.6h9.75v9.451L0 20.699M10.949 12.6H24V24l-12.9-1.801" />
227
+ </svg>
228
+ Windows
229
+ </span>
230
+ <span class="platform-chip">
231
+ <svg viewBox="0 0 24 24" fill="currentColor" width="16" height="16">
232
+ <path
233
+ d="M12.504 0c-.155 0-.311.005-.466.014C8.752.233 5.937 2.158 4.702 5.024c-1.058 2.451-.996 5.333.172 7.678.364.732.834 1.425 1.396 2.048.485.541.979 1.076 1.3 1.722.298.597.462 1.246.557 1.903.146 1.009.159 2.032.159 3.049v.456c0 .627.225 1.2.6 1.646.45.534 1.131.874 1.884.874h2.461c.754 0 1.434-.34 1.884-.874.375-.446.6-1.019.6-1.646v-.456c0-1.017.013-2.04.159-3.049.095-.657.259-1.306.557-1.903.321-.646.815-1.181 1.3-1.722.562-.623 1.032-1.316 1.396-2.048 1.168-2.345 1.23-5.227.172-7.678C18.063 2.158 15.248.233 11.962.014 11.81.005 11.659 0 11.504 0h1z" />
234
+ </svg>
235
+ Linux
236
+ </span>
237
+ </div>
238
+ </div>
239
+ </div>
240
+ </div>
241
+ </section>
242
+
243
+ <!-- ============ DOWNLOADS ============ -->
244
+ <section class="downloads" id="downloads">
245
+ <div class="container">
246
+ <span class="section__label">Descargas</span>
247
+ <h2 class="section__title">Empezá ahora</h2>
248
+ <p class="section__desc">Elegí la versión que mejor se adapte a tu necesidad.</p>
249
+ <div class="downloads__grid">
250
+ <!-- npm -->
251
+ <div class="download-card reveal">
252
+ <div class="download-card__icon download-card__icon--npm">
253
+ <svg viewBox="0 0 24 24" fill="currentColor" width="32" height="32">
254
+ <path
255
+ d="M0 7.334v8h6.666v1.332H12v-1.332h12v-8H0zm6.666 6.664H5.334v-4H3.999v4H1.335V8.667h5.331v5.331zm4 0v1.336H8.001V8.667h5.334v5.331h-2.669zm12.001 0h-1.33v-4h-1.336v4h-1.335v-4h-1.33v4h-2.671V8.667h8.002v5.331zM10.665 10H12v2.667h-1.335V10z" />
256
+ </svg>
257
+ </div>
258
+ <h3>npm (CLI)</h3>
259
+ <p>Instalá globalmente con un solo comando.</p>
260
+ <div class="copy-block copy-block--small" id="copyBlockDl">
261
+ <code>npm i -g vtex-css-sanitizer-cli</code>
262
+ <button class="copy-block__btn" id="copyBtnDl" aria-label="Copiar comando">
263
+ <svg class="copy-block__icon copy-block__icon--copy" viewBox="0 0 24 24" fill="none" stroke="currentColor"
264
+ stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
265
+ <rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
266
+ <path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1" />
267
+ </svg>
268
+ <svg class="copy-block__icon copy-block__icon--check" viewBox="0 0 24 24" fill="none"
269
+ stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
270
+ <polyline points="20 6 9 17 4 12" />
271
+ </svg>
272
+ </button>
273
+ </div>
274
+ </div>
275
+ <!-- Windows -->
276
+ <div class="download-card reveal">
277
+ <div class="download-card__icon download-card__icon--win">
278
+ <svg viewBox="0 0 24 24" fill="currentColor" width="32" height="32">
279
+ <path
280
+ d="M0 3.449L9.75 2.1v9.451H0m10.949-9.602L24 0v11.4H10.949M0 12.6h9.75v9.451L0 20.699M10.949 12.6H24V24l-12.9-1.801" />
281
+ </svg>
282
+ </div>
283
+ <h3>Windows</h3>
284
+ <p>Descargá el instalador .exe para Windows.</p>
285
+ <a href="https://github.com/emanueleelias/vtex-css-sanitizer/releases/latest" target="_blank" rel="noopener"
286
+ class="btn btn--download">
287
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
288
+ stroke-linejoin="round" width="18" height="18">
289
+ <path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4" />
290
+ <polyline points="7 10 12 15 17 10" />
291
+ <line x1="12" y1="15" x2="12" y2="3" />
292
+ </svg>
293
+ Descargar .exe
294
+ </a>
295
+ </div>
296
+ <!-- Linux -->
297
+ <div class="download-card reveal">
298
+ <div class="download-card__icon download-card__icon--linux">
299
+ <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 15 15"><!-- Icon from Teenyicons by smhmd - https://github.com/teenyicons/teenyicons/blob/master/LICENSE --><path fill="currentColor" d="m.5 14.5l-.354-.354A.5.5 0 0 0 .5 15zm.656-.656l-.353-.354zM14.5 14.5v.5a.5.5 0 0 0 .354-.854zm-11-4l-.224-.447a.5.5 0 0 0-.13.8zm.87-.435l.223.447zm6.26 0l-.223.447zm.87.435l.354.354a.5.5 0 0 0-.13-.801zm-7.414.586l.353-.354zM.854 14.854l.656-.657l-.707-.707l-.657.656zM2 13.014V6.5H1v6.514zM13 6.5v6.514h1V6.5zm.49 7.697l.656.657l.708-.708l-.657-.656zM14.5 14H.5v1h14zm-1.5-.986c0 .444.176.87.49 1.183l.707-.707a.67.67 0 0 1-.197-.476zM7.5 1A5.5 5.5 0 0 1 13 6.5h1A6.5 6.5 0 0 0 7.5 0zM2 6.5A5.5 5.5 0 0 1 7.5 1V0A6.5 6.5 0 0 0 1 6.5zm-.49 7.697c.314-.314.49-.74.49-1.183H1c0 .179-.07.35-.197.476zm2.214-3.25l.87-.434l-.448-.895l-.87.435zm6.683-.434l.87.434l.447-.894l-.87-.435zm.74-.367l-.586.586l.707.707l.586-.585zm-6.708.586l-.585-.586l-.708.708l.586.585zM7.5 12a4.33 4.33 0 0 1-3.06-1.268l-.708.707c1 1 2.355 1.561 3.768 1.561zm3.06-1.268A4.33 4.33 0 0 1 7.5 12v1a5.33 5.33 0 0 0 3.768-1.56zm-5.967-.22a6.5 6.5 0 0 1 5.814 0l.447-.894a7.5 7.5 0 0 0-6.708 0zM7 6.5v1h1v-1zm-3 1v-1H3v1zM5.5 9A1.5 1.5 0 0 1 4 7.5H3A2.5 2.5 0 0 0 5.5 10zM7 7.5A1.5 1.5 0 0 1 5.5 9v1A2.5 2.5 0 0 0 8 7.5zM5.5 5A1.5 1.5 0 0 1 7 6.5h1A2.5 2.5 0 0 0 5.5 4zm0-1A2.5 2.5 0 0 0 3 6.5h1A1.5 1.5 0 0 1 5.5 5zM11 6.5v1h1v-1zM9.5 9A1.5 1.5 0 0 1 8 7.5H7A2.5 2.5 0 0 0 9.5 10zM11 7.5A1.5 1.5 0 0 1 9.5 9v1A2.5 2.5 0 0 0 12 7.5zM9.5 5A1.5 1.5 0 0 1 11 6.5h1A2.5 2.5 0 0 0 9.5 4zm0-1A2.5 2.5 0 0 0 7 6.5h1A1.5 1.5 0 0 1 9.5 5zM5 7v1h1V7zm4 0v1h1V7z"/></svg>
300
+ </div>
301
+ <h3>Linux</h3>
302
+ <p>Descargá el AppImage para distribuciones Linux.</p>
303
+ <a href="https://github.com/emanueleelias/vtex-css-sanitizer/releases/latest" target="_blank" rel="noopener"
304
+ class="btn btn--download">
305
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
306
+ stroke-linejoin="round" width="18" height="18">
307
+ <path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4" />
308
+ <polyline points="7 10 12 15 17 10" />
309
+ <line x1="12" y1="15" x2="12" y2="3" />
310
+ </svg>
311
+ Descargar .AppImage
312
+ </a>
313
+ </div>
314
+ </div>
315
+ </div>
316
+ </section>
317
+
318
+ <!-- ============ FOOTER ============ -->
319
+ <footer class="footer">
320
+ <div class="container footer__inner">
321
+ <div class="footer__left">
322
+ <span class="footer__brand">VTEX CSS Sanitizer</span>
323
+ <span class="footer__copy">&copy; 2025 Elias Daniel Emanuele · MIT License</span>
324
+ </div>
325
+ <div class="footer__links">
326
+ <a href="https://github.com/emanueleelias/vtex-css-sanitizer" target="_blank" rel="noopener">GitHub</a>
327
+ <a href="https://www.npmjs.com/package/vtex-css-sanitizer-cli" target="_blank" rel="noopener">npm</a>
328
+ <a href="https://github.com/emanueleelias/vtex-css-sanitizer/issues" target="_blank" rel="noopener">Issues</a>
329
+ </div>
330
+ </div>
331
+ </footer>
332
+
333
+ <script src="script.js"></script>
334
+ </body>
335
+
336
+ </html>
package/docs/script.js ADDED
@@ -0,0 +1,64 @@
1
+ /* ========================================
2
+ VTEX CSS Sanitizer — Landing Page JS
3
+ ======================================== */
4
+
5
+ document.addEventListener('DOMContentLoaded', () => {
6
+ // -- Copy to clipboard --
7
+ function setupCopyButton(blockId, btnId) {
8
+ const block = document.getElementById(blockId);
9
+ const btn = document.getElementById(btnId);
10
+ if (!block || !btn) return;
11
+
12
+ btn.addEventListener('click', () => {
13
+ const code = block.querySelector('code');
14
+ if (!code) return;
15
+
16
+ const text = code.textContent.trim();
17
+ navigator.clipboard.writeText(text).then(() => {
18
+ block.classList.add('copied');
19
+ setTimeout(() => block.classList.remove('copied'), 2000);
20
+ });
21
+ });
22
+ }
23
+
24
+ setupCopyButton('copyBlock', 'copyBtn');
25
+ setupCopyButton('copyBlockDl', 'copyBtnDl');
26
+
27
+ // -- Scroll reveal --
28
+ const reveals = document.querySelectorAll('.reveal');
29
+
30
+ if ('IntersectionObserver' in window) {
31
+ const observer = new IntersectionObserver(
32
+ (entries) => {
33
+ entries.forEach((entry) => {
34
+ if (entry.isIntersecting) {
35
+ entry.target.classList.add('visible');
36
+ observer.unobserve(entry.target);
37
+ }
38
+ });
39
+ },
40
+ { threshold: 0.15, rootMargin: '0px 0px -40px 0px' }
41
+ );
42
+
43
+ reveals.forEach((el) => observer.observe(el));
44
+ } else {
45
+ // Fallback: show all
46
+ reveals.forEach((el) => el.classList.add('visible'));
47
+ }
48
+
49
+ // -- Smooth scroll for anchor links --
50
+ document.querySelectorAll('a[href^="#"]').forEach((link) => {
51
+ link.addEventListener('click', (e) => {
52
+ const targetId = link.getAttribute('href');
53
+ if (targetId === '#') return;
54
+
55
+ const target = document.querySelector(targetId);
56
+ if (target) {
57
+ e.preventDefault();
58
+ const navHeight = document.querySelector('.nav')?.offsetHeight || 64;
59
+ const top = target.getBoundingClientRect().top + window.scrollY - navHeight;
60
+ window.scrollTo({ top, behavior: 'smooth' });
61
+ }
62
+ });
63
+ });
64
+ });