design-clone 1.0.2 → 1.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.
package/SKILL.md CHANGED
@@ -104,6 +104,54 @@ python3 $HOME/.claude/skills/ui-ux-pro-max/scripts/search.py "animation hover" -
104
104
 
105
105
  **Output:** desktop.png, tablet.png, mobile.png, source.html, source.css, source-raw.css
106
106
 
107
+ ### design:clone-site
108
+
109
+ Multi-page site cloning with shared CSS and working navigation.
110
+
111
+ ```bash
112
+ /design:clone-site https://example.com
113
+ ```
114
+
115
+ **Workflow:**
116
+ ```bash
117
+ # Auto-discover pages from navigation
118
+ design-clone clone-site https://example.com
119
+
120
+ # Or specify pages manually
121
+ design-clone clone-site https://example.com --pages /,/about,/contact
122
+
123
+ # Options:
124
+ # --pages <paths> Comma-separated paths
125
+ # --max-pages <n> Limit pages (default: 10)
126
+ # --viewports <list> Viewports (default: desktop,tablet,mobile)
127
+ # --yes Skip confirmation
128
+ # --output <dir> Custom output directory
129
+ ```
130
+
131
+ **Output Structure:**
132
+ ```
133
+ cloned-designs/{timestamp}-{domain}/
134
+ ├── analysis/ # Screenshots by viewport
135
+ │ ├── desktop/*.png
136
+ │ ├── tablet/*.png
137
+ │ └── mobile/*.png
138
+ ├── pages/ # HTML with rewritten links
139
+ │ ├── index.html
140
+ │ ├── about.html
141
+ │ └── contact.html
142
+ ├── styles.css # Merged + deduplicated CSS
143
+ └── manifest.json # Page metadata + mapping
144
+ ```
145
+
146
+ **Features:**
147
+ - Auto-discovers pages from navigation (SPA-aware)
148
+ - Shared CSS with deduplication (15-30% reduction)
149
+ - Working internal links
150
+ - Progress reporting
151
+ - Graceful error handling (continues on page failures)
152
+
153
+ ---
154
+
107
155
  ### design:clone-px
108
156
 
109
157
  Pixel-perfect clone with full asset extraction and AI analysis.
@@ -223,6 +271,11 @@ GEMINI_API_KEY=your-key # Optional: enables AI structure analysis
223
271
  | screenshot.js | src/core/ | Capture screenshots + extract HTML/CSS |
224
272
  | filter-css.js | src/core/ | Filter unused CSS rules |
225
273
  | extract-assets.js | src/core/ | Download images, fonts, icons |
274
+ | discover-pages.js | src/core/ | Discover navigation links |
275
+ | multi-page-screenshot.js | src/core/ | Capture multiple pages |
276
+ | merge-css.js | src/core/ | Merge + deduplicate CSS |
277
+ | rewrite-links.js | src/core/ | Rewrite internal links |
278
+ | clone-site.js | bin/commands/ | Multi-page clone CLI |
226
279
  | analyze-structure.py | src/ai/ | AI-powered structure analysis |
227
280
  | extract-design-tokens.py | src/ai/ | Extract colors, typography, spacing |
228
281
  | verify-menu.js | src/verification/ | Validate navigation structure |
package/bin/cli.js CHANGED
@@ -5,12 +5,14 @@
5
5
  * Usage:
6
6
  * design-clone init [--force] Install skill to ~/.claude/skills/
7
7
  * design-clone verify Check installation status
8
+ * design-clone clone-site <url> [options] Clone multiple pages
8
9
  * design-clone help Show help
9
10
  */
10
11
 
11
12
  import { init } from './commands/init.js';
12
13
  import { verify } from './commands/verify.js';
13
14
  import { help } from './commands/help.js';
15
+ import { cloneSite, parseArgs as parseCloneSiteArgs, showHelp as showCloneSiteHelp } from './commands/clone-site.js';
14
16
 
15
17
  const [,, command, ...args] = process.argv;
16
18
 
@@ -25,6 +27,20 @@ async function main() {
25
27
  case 'check':
26
28
  await verify();
27
29
  break;
30
+ case 'clone-site':
31
+ if (args.includes('--help') || args.includes('-h')) {
32
+ showCloneSiteHelp();
33
+ } else {
34
+ const options = parseCloneSiteArgs(args);
35
+ if (!options.url) {
36
+ console.error('Error: URL is required');
37
+ showCloneSiteHelp();
38
+ process.exit(1);
39
+ }
40
+ const result = await cloneSite(options.url, options);
41
+ console.log(JSON.stringify(result, null, 2));
42
+ }
43
+ break;
28
44
  case 'help':
29
45
  case '--help':
30
46
  case '-h':
@@ -0,0 +1,324 @@
1
+ /**
2
+ * Clone Site Command
3
+ *
4
+ * Clone multiple pages from a website with shared CSS and working navigation.
5
+ *
6
+ * Usage:
7
+ * design-clone clone-site <url> [options]
8
+ *
9
+ * Options:
10
+ * --pages <paths> Comma-separated paths (e.g., /,/about,/contact)
11
+ * --max-pages <n> Maximum pages to clone (default: 10)
12
+ * --viewports <list> Viewport list (default: desktop,tablet,mobile)
13
+ * --yes Skip confirmation prompt
14
+ * --output <dir> Custom output directory
15
+ */
16
+
17
+ import fs from 'fs/promises';
18
+ import path from 'path';
19
+ import { fileURLToPath } from 'url';
20
+
21
+ import { discoverPages } from '../../src/core/discover-pages.js';
22
+ import { captureMultiplePages } from '../../src/core/multi-page-screenshot.js';
23
+ import { mergeCssFiles } from '../../src/core/merge-css.js';
24
+ import { rewriteLinks, createPageManifest, rewriteAllLinks } from '../../src/core/rewrite-links.js';
25
+ import { extractDesignTokens } from '../../src/core/design-tokens.js';
26
+
27
+ /**
28
+ * Generate output directory name
29
+ * @param {string} url - Target URL
30
+ * @returns {string} Output directory path
31
+ */
32
+ function generateOutputDir(url) {
33
+ const urlObj = new URL(url);
34
+ const domain = urlObj.hostname.replace(/^www\./, '');
35
+ const timestamp = new Date().toISOString()
36
+ .replace(/[-:]/g, '')
37
+ .replace('T', '-')
38
+ .slice(0, 13);
39
+
40
+ return `./cloned-designs/${timestamp}-${domain}`;
41
+ }
42
+
43
+ /**
44
+ * Parse CLI arguments
45
+ * @param {string[]} args - CLI arguments
46
+ * @returns {Object} Parsed options
47
+ */
48
+ export function parseArgs(args) {
49
+ const options = {
50
+ url: null,
51
+ pages: null,
52
+ maxPages: 10,
53
+ viewports: ['desktop', 'tablet', 'mobile'],
54
+ skipConfirm: false,
55
+ output: null,
56
+ ai: false
57
+ };
58
+
59
+ for (let i = 0; i < args.length; i++) {
60
+ const arg = args[i];
61
+
62
+ if (arg === '--pages' && args[i + 1]) {
63
+ options.pages = args[++i].split(',').map(p => p.trim());
64
+ } else if (arg === '--max-pages' && args[i + 1]) {
65
+ options.maxPages = parseInt(args[++i], 10);
66
+ } else if (arg === '--viewports' && args[i + 1]) {
67
+ options.viewports = args[++i].split(',').map(v => v.trim());
68
+ } else if (arg === '--yes' || arg === '-y') {
69
+ options.skipConfirm = true;
70
+ } else if (arg === '--output' && args[i + 1]) {
71
+ options.output = args[++i];
72
+ } else if (arg === '--ai') {
73
+ options.ai = true;
74
+ } else if (!arg.startsWith('--') && !options.url) {
75
+ options.url = arg;
76
+ }
77
+ }
78
+
79
+ return options;
80
+ }
81
+
82
+ /**
83
+ * Clone multiple pages from a website
84
+ * @param {string} url - Target URL
85
+ * @param {Object} options - Clone options
86
+ * @returns {Promise<Object>} Clone result
87
+ */
88
+ export async function cloneSite(url, options = {}) {
89
+ const startTime = Date.now();
90
+ const {
91
+ pages: manualPages,
92
+ maxPages = 10,
93
+ viewports = ['desktop', 'tablet', 'mobile'],
94
+ skipConfirm = false,
95
+ output,
96
+ ai = false
97
+ } = options;
98
+
99
+ // Validate URL
100
+ let baseUrl;
101
+ try {
102
+ baseUrl = new URL(url);
103
+ } catch {
104
+ throw new Error(`Invalid URL: ${url}`);
105
+ }
106
+
107
+ // Generate output directory
108
+ const outputDir = output || generateOutputDir(url);
109
+
110
+ console.error(`\n[clone-site] Target: ${url}`);
111
+ console.error(`[clone-site] Output: ${outputDir}`);
112
+
113
+ // Step 1: Discover or use manual pages
114
+ console.error('\n[1/6] Discovering pages...');
115
+
116
+ let pageList;
117
+ if (manualPages && manualPages.length > 0) {
118
+ // Use manual page list
119
+ pageList = {
120
+ success: true,
121
+ pages: manualPages.map(p => ({
122
+ path: p,
123
+ name: p === '/' ? 'Home' : p.replace(/^\//, '').replace(/-/g, ' '),
124
+ url: new URL(p, url).href
125
+ }))
126
+ };
127
+ console.error(` Using ${pageList.pages.length} manual pages`);
128
+ } else {
129
+ // Auto-discover
130
+ pageList = await discoverPages(url, { maxPages });
131
+ if (!pageList.success) {
132
+ console.error(` Warning: Discovery failed - ${pageList.error}`);
133
+ console.error(' Falling back to homepage only');
134
+ }
135
+ console.error(` Found ${pageList.pages.length} pages`);
136
+ }
137
+
138
+ // Show discovered pages
139
+ for (const page of pageList.pages) {
140
+ console.error(` - ${page.path} (${page.name})`);
141
+ }
142
+
143
+ // Step 2: Capture all pages
144
+ console.error('\n[2/6] Capturing pages...');
145
+
146
+ const captureResult = await captureMultiplePages(pageList.pages, {
147
+ outputDir,
148
+ viewports,
149
+ onProgress: (current, total, info) => {
150
+ console.error(` [${current}/${total}] ${info.status}: ${info.name}`);
151
+ }
152
+ });
153
+
154
+ if (!captureResult.success) {
155
+ throw new Error(`Capture failed: ${captureResult.error}`);
156
+ }
157
+
158
+ console.error(` Captured ${captureResult.stats.successfulPages}/${captureResult.stats.totalPages} pages`);
159
+ console.error(` Screenshots: ${captureResult.stats.totalScreenshots}`);
160
+
161
+ // Step 3: Merge CSS files (prefer filtered CSS)
162
+ console.error('\n[3/6] Merging CSS...');
163
+
164
+ const mergedCssPath = path.join(outputDir, 'styles.css');
165
+ let mergeResult = { success: false };
166
+
167
+ // Use filtered CSS if available, fallback to raw CSS
168
+ const cssToMerge = captureResult.cssFilesFiltered?.length > 0
169
+ ? captureResult.cssFilesFiltered
170
+ : captureResult.cssFiles;
171
+
172
+ const cssType = captureResult.cssFilesFiltered?.length > 0 ? 'filtered' : 'raw';
173
+
174
+ if (cssToMerge.length > 0) {
175
+ mergeResult = await mergeCssFiles(cssToMerge, mergedCssPath);
176
+ if (mergeResult.success) {
177
+ console.error(` Merged ${mergeResult.input.fileCount} ${cssType} files`);
178
+ console.error(` Reduction: ${mergeResult.stats.reduction}`);
179
+ } else {
180
+ console.error(` Warning: Merge failed - ${mergeResult.error}`);
181
+ }
182
+ } else {
183
+ console.error(' No CSS files to merge');
184
+ }
185
+
186
+ // Step 4: Extract design tokens (if --ai flag)
187
+ console.error('\n[4/6] Extracting design tokens...');
188
+
189
+ let hasTokens = false;
190
+ if (ai) {
191
+ if (process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY) {
192
+ const tokenResult = await extractDesignTokens(outputDir, mergedCssPath);
193
+ if (tokenResult.success) {
194
+ hasTokens = true;
195
+ console.error(` Created: tokens.css, design-tokens.json`);
196
+ } else {
197
+ console.error(` Warning: Token extraction failed - ${tokenResult.error}`);
198
+ if (tokenResult.hint) {
199
+ console.error(` Hint: ${tokenResult.hint}`);
200
+ }
201
+ }
202
+ } else {
203
+ console.error(' Skipped: GEMINI_API_KEY not set');
204
+ console.error(' Hint: Set GEMINI_API_KEY in ~/.claude/.env for AI token extraction');
205
+ }
206
+ } else {
207
+ console.error(' Skipped (use --ai flag to enable)');
208
+ }
209
+
210
+ // Step 5: Rewrite links
211
+ console.error('\n[5/6] Rewriting links...');
212
+
213
+ const manifest = createPageManifest(pageList.pages, {
214
+ hasTokens,
215
+ stats: {
216
+ totalPages: pageList.pages.length,
217
+ totalScreenshots: captureResult.stats.totalScreenshots,
218
+ cssReduction: mergeResult.stats?.reduction || '0%',
219
+ captureTimeMs: captureResult.stats.totalTimeMs
220
+ }
221
+ });
222
+
223
+ // Copy HTML files to pages/ directory and rewrite links
224
+ const pagesDir = path.join(outputDir, 'pages');
225
+ await fs.mkdir(pagesDir, { recursive: true });
226
+
227
+ for (const page of manifest.pages) {
228
+ const sourceHtml = path.join(outputDir, 'html', page.file);
229
+ const destHtml = path.join(pagesDir, page.file);
230
+
231
+ try {
232
+ let html = await fs.readFile(sourceHtml, 'utf-8');
233
+ html = rewriteLinks(html, manifest, {
234
+ baseUrl: url,
235
+ injectTokensCss: hasTokens
236
+ });
237
+ await fs.writeFile(destHtml, html, 'utf-8');
238
+ console.error(` Rewritten: ${page.file}`);
239
+ } catch (err) {
240
+ console.error(` Warning: Failed to rewrite ${page.file}: ${err.message}`);
241
+ }
242
+ }
243
+
244
+ // Step 6: Generate manifest
245
+ console.error('\n[6/6] Generating manifest...');
246
+
247
+ const manifestPath = path.join(outputDir, 'manifest.json');
248
+ await fs.writeFile(manifestPath, JSON.stringify(manifest, null, 2));
249
+ console.error(` Created: manifest.json`);
250
+
251
+ // Summary
252
+ const totalTime = Date.now() - startTime;
253
+ console.error(`\n[clone-site] Complete!`);
254
+ console.error(` Output: ${path.resolve(outputDir)}`);
255
+ console.error(` Pages: ${manifest.pages.length}`);
256
+ console.error(` Time: ${(totalTime / 1000).toFixed(1)}s`);
257
+
258
+ return {
259
+ success: true,
260
+ outputDir: path.resolve(outputDir),
261
+ manifest,
262
+ captureResult,
263
+ mergeResult,
264
+ totalTimeMs: totalTime
265
+ };
266
+ }
267
+
268
+ /**
269
+ * Show help message
270
+ */
271
+ export function showHelp() {
272
+ console.log(`
273
+ Usage: design-clone clone-site <url> [options]
274
+
275
+ Clone multiple pages from a website with shared CSS and working navigation.
276
+
277
+ Options:
278
+ --pages <paths> Comma-separated paths (e.g., /,/about,/contact)
279
+ --max-pages <n> Maximum pages to auto-discover (default: 10)
280
+ --viewports <list> Viewport list (default: desktop,tablet,mobile)
281
+ --yes Skip confirmation prompt
282
+ --output <dir> Custom output directory
283
+ --ai Extract design tokens using Gemini AI (requires GEMINI_API_KEY)
284
+
285
+ Examples:
286
+ design-clone clone-site https://example.com
287
+ design-clone clone-site https://example.com --max-pages 5
288
+ design-clone clone-site https://example.com --pages /,/about,/contact
289
+ design-clone clone-site https://example.com --ai
290
+ `);
291
+ }
292
+
293
+ // CLI entry point
294
+ const isMainModule = process.argv[1] && (
295
+ process.argv[1].endsWith('clone-site.js') ||
296
+ process.argv[1].includes('clone-site')
297
+ );
298
+
299
+ if (isMainModule) {
300
+ const args = process.argv.slice(2);
301
+
302
+ if (args.includes('--help') || args.includes('-h')) {
303
+ showHelp();
304
+ process.exit(0);
305
+ }
306
+
307
+ const options = parseArgs(args);
308
+
309
+ if (!options.url) {
310
+ console.error('Error: URL is required');
311
+ showHelp();
312
+ process.exit(1);
313
+ }
314
+
315
+ cloneSite(options.url, options)
316
+ .then(result => {
317
+ console.log(JSON.stringify(result, null, 2));
318
+ process.exit(0);
319
+ })
320
+ .catch(err => {
321
+ console.error(`\n[ERROR] ${err.message}`);
322
+ process.exit(1);
323
+ });
324
+ }
@@ -9,16 +9,28 @@ design-clone - Claude Code skill for website design cloning
9
9
  Usage:
10
10
  design-clone init [options] Install skill to ~/.claude/skills/
11
11
  design-clone verify Check installation status
12
+ design-clone clone-site <url> Clone multiple pages from a website
12
13
  design-clone help Show this help
13
14
 
14
- Options:
15
+ Init Options:
15
16
  --force, -f Overwrite existing installation
16
17
  --skip-deps Skip dependency installation
17
18
 
19
+ Clone-site Options:
20
+ --pages <paths> Comma-separated paths (e.g., /,/about,/contact)
21
+ --max-pages <n> Maximum pages to auto-discover (default: 10)
22
+ --viewports <list> Viewport list (default: desktop,tablet,mobile)
23
+ --yes Skip confirmation prompt
24
+ --output <dir> Custom output directory
25
+ --ai Extract design tokens using Gemini AI (requires GEMINI_API_KEY)
26
+
18
27
  Examples:
19
- design-clone init # Install skill
20
- design-clone init --force # Reinstall, overwrite existing
21
- design-clone verify # Check if installed correctly
28
+ design-clone init # Install skill
29
+ design-clone init --force # Reinstall, overwrite existing
30
+ design-clone verify # Check if installed correctly
31
+ design-clone clone-site https://example.com
32
+ design-clone clone-site https://example.com --max-pages 5
33
+ design-clone clone-site https://example.com --pages /,/about,/contact
22
34
 
23
35
  After installation:
24
36
  1. Set GEMINI_API_KEY in ~/.claude/.env (optional, for AI analysis)
@@ -95,3 +95,75 @@ GEMINI_API_KEY=your-api-key-here
95
95
  ```bash
96
96
  --viewports '[{"width":1440,"height":900,"name":"custom"}]'
97
97
  ```
98
+
99
+ ## clone-site Issues
100
+
101
+ ### No pages discovered
102
+
103
+ **Symptom:** Only homepage cloned, other pages not found.
104
+
105
+ **Causes:**
106
+ - Site uses JS-rendered navigation (React/Vue/Angular)
107
+ - Navigation not in standard selectors (header nav, footer nav)
108
+
109
+ **Solutions:**
110
+ ```bash
111
+ # Specify pages manually
112
+ design-clone clone-site https://example.com --pages /,/about,/contact,/services
113
+
114
+ # Increase max pages if hitting limit
115
+ design-clone clone-site https://example.com --max-pages 20
116
+ ```
117
+
118
+ ### Links not working in cloned pages
119
+
120
+ **Symptom:** Internal links point to original URLs.
121
+
122
+ **Causes:**
123
+ - Page not in discovered list
124
+ - HTML file not found for rewriting
125
+
126
+ **Solutions:**
127
+ 1. Check `manifest.json` for page list
128
+ 2. Ensure all pages captured successfully (check `capture-results.json`)
129
+ 3. Re-run with manual `--pages` flag including missing pages
130
+
131
+ ### CSS broken on some pages
132
+
133
+ **Symptom:** Styling differs between cloned pages.
134
+
135
+ **Causes:**
136
+ - Page-specific CSS not merged
137
+ - CSS extraction failed for some pages
138
+
139
+ **Solutions:**
140
+ 1. Check `css/` folder for per-page CSS files
141
+ 2. Review merge stats in output
142
+ 3. Try with fewer pages to isolate issue
143
+
144
+ ### Timeout during capture
145
+
146
+ **Error:** `Navigation timeout`
147
+
148
+ **Causes:**
149
+ - Large pages
150
+ - Slow server
151
+ - Too many pages
152
+
153
+ **Solutions:**
154
+ ```bash
155
+ # Reduce pages
156
+ design-clone clone-site https://example.com --max-pages 5
157
+
158
+ # Use specific viewports only
159
+ design-clone clone-site https://example.com --viewports desktop
160
+ ```
161
+
162
+ ### Memory issues
163
+
164
+ **Symptom:** Process crashes or hangs.
165
+
166
+ **Solutions:**
167
+ 1. Reduce `--max-pages` to 5 or fewer
168
+ 2. Clone in batches
169
+ 3. Close other applications
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "design-clone",
3
- "version": "1.0.2",
3
+ "version": "1.1.0",
4
4
  "type": "module",
5
5
  "description": "Claude Code skill for cloning website designs via multi-viewport screenshots, HTML/CSS extraction, and Gemini AI analysis",
6
6
  "bin": {
@@ -9,6 +9,27 @@
9
9
  export const MAX_CSS_SIZE = 5 * 1024 * 1024; // 5MB limit
10
10
  export const MAX_CSS_RULES_WARN = 5000; // Warn on large stylesheets
11
11
 
12
+ // Layout-critical properties for accurate cloning
13
+ export const LAYOUT_PROPERTIES = {
14
+ // Display & Flex
15
+ display: ['display', 'flexDirection', 'flexWrap', 'justifyContent',
16
+ 'alignItems', 'alignContent', 'gap', 'rowGap', 'columnGap'],
17
+ // Grid
18
+ grid: ['gridTemplateColumns', 'gridTemplateRows', 'gridGap', 'gridAutoFlow'],
19
+ // Position
20
+ position: ['position', 'top', 'right', 'bottom', 'left', 'zIndex'],
21
+ // Sizing
22
+ sizing: ['width', 'height', 'minWidth', 'maxWidth', 'minHeight', 'maxHeight'],
23
+ // Box Model
24
+ box: ['boxSizing', 'overflow', 'overflowX', 'overflowY', 'borderWidth', 'borderStyle'],
25
+ // Visual (existing)
26
+ visual: ['color', 'backgroundColor', 'fontFamily', 'fontSize',
27
+ 'fontWeight', 'lineHeight', 'padding', 'margin', 'borderRadius']
28
+ };
29
+
30
+ // Flatten for iteration
31
+ export const ALL_PROPERTIES = Object.values(LAYOUT_PROPERTIES).flat();
32
+
12
33
  /**
13
34
  * Extract all CSS from page
14
35
  * @param {Page} page - Puppeteer page
@@ -16,7 +37,7 @@ export const MAX_CSS_RULES_WARN = 5000; // Warn on large stylesheets
16
37
  * @returns {Promise<{cssBlocks: Array, corsBlocked: Array, computedStyles: Object, totalRules: number, warnings: Array}>}
17
38
  */
18
39
  export async function extractAllCss(page, baseUrl) {
19
- return await page.evaluate((url) => {
40
+ return await page.evaluate((url, allProps) => {
20
41
  const cssBlocks = [];
21
42
  const corsBlocked = [];
22
43
  const warnings = [];
@@ -77,17 +98,21 @@ export async function extractAllCss(page, baseUrl) {
77
98
  const el = document.querySelector(selector);
78
99
  if (el) {
79
100
  const style = getComputedStyle(el);
80
- computedStyles[selector] = {
81
- color: style.color,
82
- backgroundColor: style.backgroundColor,
83
- fontFamily: style.fontFamily,
84
- fontSize: style.fontSize,
85
- fontWeight: style.fontWeight,
86
- lineHeight: style.lineHeight,
87
- padding: style.padding,
88
- margin: style.margin,
89
- borderRadius: style.borderRadius
90
- };
101
+ const styles = {};
102
+
103
+ // Extract all layout + visual properties
104
+ allProps.forEach(prop => {
105
+ const value = style[prop];
106
+ // Skip empty/default values to reduce payload (except display)
107
+ if (prop === 'display') {
108
+ styles[prop] = value; // Always include display for inline strategy
109
+ } else if (value && value !== 'none' && value !== 'auto' &&
110
+ value !== 'normal' && value !== '0px' && value !== 'static') {
111
+ styles[prop] = value;
112
+ }
113
+ });
114
+
115
+ computedStyles[selector] = styles;
91
116
  }
92
117
  } catch (e) {
93
118
  // Ignore invalid selectors
@@ -103,5 +128,5 @@ export async function extractAllCss(page, baseUrl) {
103
128
  totalRules,
104
129
  warnings
105
130
  };
106
- }, baseUrl);
131
+ }, baseUrl, ALL_PROPERTIES);
107
132
  }