designlang 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "design-extract",
3
+ "owner": {
4
+ "name": "Manavarya Singh",
5
+ "url": "https://github.com/Manavarya09"
6
+ },
7
+ "plugins": [
8
+ {
9
+ "name": "design-extract",
10
+ "source": "./",
11
+ "description": "Extract the complete design language from any website — colors, typography, spacing, shadows, components, and more. Outputs AI-optimized markdown, W3C design tokens, Tailwind config, and CSS variables.",
12
+ "version": "1.0.0",
13
+ "author": {
14
+ "name": "Manavarya Singh"
15
+ },
16
+ "category": "design",
17
+ "tags": [
18
+ "design-system",
19
+ "design-tokens",
20
+ "css",
21
+ "tailwind",
22
+ "typography",
23
+ "colors",
24
+ "web-scraping"
25
+ ]
26
+ }
27
+ ]
28
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "design-extract",
3
+ "description": "Extract the complete design language from any website. Produces W3C design tokens, AI-optimized markdown, Tailwind config, and CSS custom properties.",
4
+ "version": "1.0.0",
5
+ "author": {
6
+ "name": "Manavarya Singh",
7
+ "url": "https://github.com/Manavarya09"
8
+ },
9
+ "homepage": "https://github.com/Manavarya09/design-extract",
10
+ "repository": "https://github.com/Manavarya09/design-extract",
11
+ "license": "MIT",
12
+ "keywords": [
13
+ "design-system",
14
+ "design-tokens",
15
+ "design-language",
16
+ "css",
17
+ "tailwind",
18
+ "playwright",
19
+ "extraction",
20
+ "colors",
21
+ "typography"
22
+ ],
23
+ "skills": "./skills/"
24
+ }
@@ -0,0 +1,63 @@
1
+ # Contributing to design-ex
2
+
3
+ Thanks for your interest in contributing! Here's how to get started.
4
+
5
+ ## Development Setup
6
+
7
+ ```bash
8
+ git clone https://github.com/Manavarya09/design-extract.git
9
+ cd design-extract
10
+ npm install
11
+ ```
12
+
13
+ This installs dependencies and Playwright's Chromium browser.
14
+
15
+ ## Running Locally
16
+
17
+ ```bash
18
+ node bin/design-extract.js https://example.com --out ./test-output
19
+ ```
20
+
21
+ ## Project Structure
22
+
23
+ ```
24
+ src/
25
+ crawler.js # Playwright page.evaluate extraction
26
+ index.js # Orchestrator
27
+ utils.js # Color parsing, clustering, helpers
28
+ extractors/ # 9 modules that process raw style data
29
+ colors.js # Color palette extraction
30
+ typography.js # Font and type scale extraction
31
+ spacing.js # Spacing scale detection
32
+ shadows.js # Box shadow parsing
33
+ borders.js # Border radius extraction
34
+ variables.js # CSS custom property categorization
35
+ breakpoints.js # Media query extraction
36
+ animations.js # Transition and keyframe extraction
37
+ components.js # UI component pattern detection
38
+ formatters/ # 4 output format generators
39
+ markdown.js # AI-optimized markdown (hero output)
40
+ tokens.js # W3C Design Tokens JSON
41
+ tailwind.js # Tailwind CSS config
42
+ css-vars.js # CSS custom properties file
43
+ ```
44
+
45
+ ## Guidelines
46
+
47
+ - Keep dependencies minimal — prefer pure JS over adding a package
48
+ - Test changes against at least 2-3 real websites
49
+ - Follow the existing code style (ES modules, no semicolons in some files)
50
+ - The markdown formatter is the most important output — keep it rich and AI-friendly
51
+
52
+ ## Reporting Issues
53
+
54
+ When filing a bug, please include:
55
+ - The URL you tried to extract from
56
+ - The error message or unexpected output
57
+ - Your Node.js version (`node --version`)
58
+
59
+ ## Pull Requests
60
+
61
+ - One feature/fix per PR
62
+ - Keep PRs focused and small when possible
63
+ - Add a brief description of what changed and why
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Manavarya Singh
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,153 @@
1
+ <p align="center">
2
+ <h1 align="center">designlang</h1>
3
+ <p align="center">Extract the complete design language from any website.</p>
4
+ </p>
5
+
6
+ <p align="center">
7
+ <a href="https://www.npmjs.com/package/designlang"><img src="https://img.shields.io/npm/v/designlang?color=blue&label=npm" alt="npm version"></a>
8
+ <a href="https://github.com/Manavarya09/designlangtract/blob/main/LICENSE"><img src="https://img.shields.io/github/license/Manavarya09/designlangtract" alt="license"></a>
9
+ <a href="https://nodejs.org"><img src="https://img.shields.io/node/v/designlang" alt="node version"></a>
10
+ </p>
11
+
12
+ ---
13
+
14
+ **designlang** uses Playwright to headlessly crawl any website, extracts computed styles from the live DOM, and generates a full design system you can immediately use in your project. The primary output is an **AI-optimized markdown file** that an LLM can use to faithfully recreate the design.
15
+
16
+ ## Quick Start
17
+
18
+ ```bash
19
+ npx designlang https://stripe.com
20
+ ```
21
+
22
+ That's it. Four files appear in `./designlangtract-output/`:
23
+
24
+ | File | What it is |
25
+ |------|------------|
26
+ | `*-design-language.md` | AI-optimized markdown — the full design system in natural language with code examples |
27
+ | `*-design-tokens.json` | [W3C Design Tokens](https://design-tokens.github.io/community-group/format/) format for tooling |
28
+ | `*-tailwind.config.js` | Drop-in Tailwind CSS theme extension |
29
+ | `*-variables.css` | CSS custom properties ready to import |
30
+
31
+ ## Install
32
+
33
+ ```bash
34
+ # Use directly with npx (no install needed)
35
+ npx designlang https://example.com
36
+
37
+ # Or install globally
38
+ npm install -g designlang
39
+ designlang https://example.com
40
+ ```
41
+
42
+ > Playwright's Chromium is auto-installed on first run via the `postinstall` script.
43
+
44
+ ## What It Extracts
45
+
46
+ | Category | Details |
47
+ |----------|---------|
48
+ | **Colors** | Full palette with primary/secondary/accent/neutral classification, gradients, background & text colors |
49
+ | **Typography** | Font families, type scale (heading/body/caption), weights, line heights, letter spacing |
50
+ | **Spacing** | All unique values with automatic base-unit detection (e.g. 4px grid) |
51
+ | **Border Radii** | Unique values labeled xs through full |
52
+ | **Box Shadows** | Parsed and classified by visual weight (xs/sm/md/lg/xl) |
53
+ | **CSS Variables** | All `:root` custom properties, categorized by type |
54
+ | **Breakpoints** | Media query breakpoints with standard labels (sm/md/lg/xl) |
55
+ | **Animations** | Transitions, easing functions, durations, `@keyframes` |
56
+ | **Components** | Detected patterns for buttons, cards, inputs, links — with base styles |
57
+
58
+ ## CLI Options
59
+
60
+ ```
61
+ designlang <url> [options]
62
+
63
+ Options:
64
+ -o, --out <dir> Output directory (default: ./designlangtract-output)
65
+ -n, --name <name> Output file prefix (default: derived from URL hostname)
66
+ -w, --width <px> Viewport width (default: 1280)
67
+ -h, --height <px> Viewport height (default: 800)
68
+ --wait <ms> Wait after page load for SPAs (default: 0)
69
+ --dark Also extract dark mode color scheme
70
+ --verbose Show detailed extraction progress
71
+ -V, --version Show version
72
+ -h, --help Show help
73
+ ```
74
+
75
+ ### Examples
76
+
77
+ ```bash
78
+ # Basic extraction
79
+ designlang https://vercel.com
80
+
81
+ # Custom output directory
82
+ designlang https://stripe.com --out ./stripe-design
83
+
84
+ # Extract dark mode too
85
+ designlang https://github.com --dark
86
+
87
+ # Wait for SPA to render
88
+ designlang https://app.example.com --wait 3000
89
+
90
+ # Custom viewport
91
+ designlang https://example.com --width 1440 --height 900
92
+ ```
93
+
94
+ ## The Markdown Output
95
+
96
+ The `*-design-language.md` file is the hero output. It's structured for AI/LLM consumption with:
97
+
98
+ - Color palette tables with hex, RGB, and HSL values
99
+ - Typography scale with size, weight, line-height, and letter-spacing
100
+ - Spacing scale with token names and px/rem conversions
101
+ - CSS code blocks for shadows, component patterns, and animations
102
+ - A "Quick Start" section with step-by-step instructions to recreate the design
103
+
104
+ Feed it to any AI coding assistant and it can recreate the site's visual design from scratch.
105
+
106
+ ## Claude Code Plugin
107
+
108
+ **designlang** also works as a [Claude Code](https://claude.ai/claude-code) plugin.
109
+
110
+ After installing, use the `/extract-design` slash command:
111
+
112
+ ```
113
+ /extract-design https://stripe.com
114
+ ```
115
+
116
+ Claude will extract the design, read the markdown output, and help you integrate it into your project.
117
+
118
+ ## How It Works
119
+
120
+ 1. **Crawl** — Launches headless Chromium via Playwright, navigates to the URL, waits for network idle and font loading
121
+ 2. **Extract** — Runs a single `page.evaluate()` call that walks up to 5,000 DOM elements and collects computed styles, CSS custom properties, media queries, and keyframes
122
+ 3. **Process** — Nine extractor modules parse, deduplicate, cluster, and classify the raw style data into a unified design object
123
+ 4. **Format** — Four formatter modules generate the output files
124
+
125
+ ## Limitations
126
+
127
+ - **Cross-origin stylesheets** — CSS loaded from CDNs may not be inspectable via `document.styleSheets` (CORS). Computed styles are still captured since `getComputedStyle()` sees the final resolved values.
128
+ - **Shadow DOM** — Elements inside closed shadow roots are not accessible. Open shadow roots are partially supported.
129
+ - **CSS-in-JS** — Styles injected at runtime (styled-components, Emotion) are captured via computed styles but not as raw CSS rules.
130
+ - **Element cap** — DOM traversal is capped at 5,000 elements to prevent hanging on very large pages.
131
+
132
+ ## Tech Stack
133
+
134
+ - [Playwright](https://playwright.dev/) — headless browser automation
135
+ - [Commander](https://github.com/tj/commander.js/) — CLI framework
136
+ - [Chalk](https://github.com/chalk/chalk) + [Ora](https://github.com/sindresorhus/ora) — terminal styling
137
+
138
+ Zero external dependencies for color parsing, clustering, or CSS processing — all handled with ~200 lines of pure JS utilities.
139
+
140
+ ## Contributing
141
+
142
+ Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
143
+
144
+ 1. Fork the repo
145
+ 2. Create a feature branch (`git checkout -b feature/my-feature`)
146
+ 3. Make your changes
147
+ 4. Test on a few websites (`node bin/designlangtract.js https://example.com`)
148
+ 5. Commit and push
149
+ 6. Open a pull request
150
+
151
+ ## License
152
+
153
+ [MIT](LICENSE) - Manavarya Singh
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from 'commander';
4
+ import { mkdirSync, writeFileSync } from 'fs';
5
+ import { resolve, join } from 'path';
6
+ import chalk from 'chalk';
7
+ import ora from 'ora';
8
+ import { extractDesignLanguage } from '../src/index.js';
9
+ import { formatMarkdown } from '../src/formatters/markdown.js';
10
+ import { formatTokens } from '../src/formatters/tokens.js';
11
+ import { formatTailwind } from '../src/formatters/tailwind.js';
12
+ import { formatCssVars } from '../src/formatters/css-vars.js';
13
+ import { nameFromUrl } from '../src/utils.js';
14
+
15
+ const program = new Command();
16
+
17
+ program
18
+ .name('designlang')
19
+ .description('Extract the complete design language from any website')
20
+ .version('1.0.0')
21
+ .argument('<url>', 'URL to extract design language from')
22
+ .option('-o, --out <dir>', 'output directory', './design-extract-output')
23
+ .option('-n, --name <name>', 'output file prefix (default: derived from URL)')
24
+ .option('-w, --width <px>', 'viewport width', parseInt, 1280)
25
+ .option('-h, --height <px>', 'viewport height', parseInt, 800)
26
+ .option('--wait <ms>', 'wait after page load (ms)', parseInt, 0)
27
+ .option('--dark', 'also extract dark mode styles')
28
+ .option('--verbose', 'show detailed progress')
29
+ .action(async (url, opts) => {
30
+ // Ensure URL has protocol
31
+ if (!url.startsWith('http')) url = `https://${url}`;
32
+
33
+ const prefix = opts.name || nameFromUrl(url);
34
+ const outDir = resolve(opts.out);
35
+
36
+ console.log('');
37
+ console.log(chalk.bold(' design-extract'));
38
+ console.log(chalk.gray(` ${url}`));
39
+ console.log('');
40
+
41
+ const spinner = ora('Launching browser...').start();
42
+
43
+ try {
44
+ spinner.text = 'Crawling page and extracting styles...';
45
+ const design = await extractDesignLanguage(url, {
46
+ width: opts.width,
47
+ height: opts.height,
48
+ wait: opts.wait,
49
+ dark: opts.dark,
50
+ });
51
+
52
+ spinner.text = 'Generating output files...';
53
+
54
+ mkdirSync(outDir, { recursive: true });
55
+
56
+ const files = [
57
+ { name: `${prefix}-design-language.md`, content: formatMarkdown(design), label: 'Markdown (AI-optimized)' },
58
+ { name: `${prefix}-design-tokens.json`, content: formatTokens(design), label: 'Design Tokens (W3C)' },
59
+ { name: `${prefix}-tailwind.config.js`, content: formatTailwind(design), label: 'Tailwind Config' },
60
+ { name: `${prefix}-variables.css`, content: formatCssVars(design), label: 'CSS Variables' },
61
+ ];
62
+
63
+ for (const file of files) {
64
+ writeFileSync(join(outDir, file.name), file.content, 'utf-8');
65
+ }
66
+
67
+ spinner.succeed('Extraction complete!');
68
+ console.log('');
69
+ console.log(chalk.bold(' Output files:'));
70
+ for (const file of files) {
71
+ const size = Buffer.byteLength(file.content);
72
+ const sizeStr = size > 1024 ? `${(size / 1024).toFixed(1)}KB` : `${size}B`;
73
+ console.log(` ${chalk.green('✓')} ${chalk.cyan(file.name)} ${chalk.gray(`(${sizeStr})`)} — ${file.label}`);
74
+ }
75
+ console.log('');
76
+ console.log(chalk.gray(` Saved to ${outDir}`));
77
+
78
+ // Summary stats
79
+ console.log('');
80
+ console.log(chalk.bold(' Summary:'));
81
+ console.log(` ${chalk.gray('Colors:')} ${design.colors.all.length} unique colors`);
82
+ console.log(` ${chalk.gray('Fonts:')} ${design.typography.families.map(f => f.name).join(', ') || 'none detected'}`);
83
+ console.log(` ${chalk.gray('Spacing:')} ${design.spacing.scale.length} values${design.spacing.base ? ` (base: ${design.spacing.base}px)` : ''}`);
84
+ console.log(` ${chalk.gray('Shadows:')} ${design.shadows.values.length} unique shadows`);
85
+ console.log(` ${chalk.gray('Radii:')} ${design.borders.radii.length} unique values`);
86
+ console.log(` ${chalk.gray('Breakpoints:')} ${design.breakpoints.length} breakpoints`);
87
+ console.log(` ${chalk.gray('Components:')} ${Object.keys(design.components).length} patterns detected`);
88
+ console.log(` ${chalk.gray('CSS Vars:')} ${Object.values(design.variables).reduce((s, v) => s + Object.keys(v).length, 0)} custom properties`);
89
+ console.log('');
90
+
91
+ } catch (err) {
92
+ spinner.fail('Extraction failed');
93
+ if (err.message.includes('playwright')) {
94
+ console.error(chalk.red('\n Playwright is not installed.'));
95
+ console.error(chalk.gray(' Run: npx playwright install chromium\n'));
96
+ } else {
97
+ console.error(chalk.red(`\n ${err.message}\n`));
98
+ if (opts.verbose) console.error(err.stack);
99
+ }
100
+ process.exit(1);
101
+ }
102
+ });
103
+
104
+ program.parse();
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "designlang",
3
+ "version": "1.0.0",
4
+ "description": "Extract the complete design language from any website — colors, typography, spacing, shadows, and more. Outputs AI-optimized markdown, W3C design tokens, Tailwind config, and CSS variables.",
5
+ "type": "module",
6
+ "bin": {
7
+ "designlang": "./bin/design-extract.js"
8
+ },
9
+ "main": "src/index.js",
10
+ "scripts": {
11
+ "postinstall": "npx playwright install chromium --with-deps 2>/dev/null || npx playwright install chromium",
12
+ "start": "node bin/design-extract.js",
13
+ "test": "node --test tests/"
14
+ },
15
+ "dependencies": {
16
+ "playwright": "^1.42.0",
17
+ "commander": "^12.0.0",
18
+ "chalk": "^5.3.0",
19
+ "ora": "^8.0.0"
20
+ },
21
+ "engines": {
22
+ "node": ">=18"
23
+ },
24
+ "keywords": [
25
+ "design-system",
26
+ "design-tokens",
27
+ "design-language",
28
+ "css",
29
+ "tailwind",
30
+ "playwright",
31
+ "extraction",
32
+ "colors",
33
+ "typography",
34
+ "claude-code",
35
+ "plugin"
36
+ ],
37
+ "author": "masyv",
38
+ "license": "MIT"
39
+ }
@@ -0,0 +1,59 @@
1
+ ---
2
+ name: extract-design
3
+ description: "Extract the full design language from any website URL. Produces AI-optimized markdown, W3C design tokens, Tailwind config, and CSS variables. Use when user says 'extract design', 'get design system', 'design language', 'design tokens', 'what colors does this site use', 'what font does this site use', or '/extract-design'."
4
+ argument-hint: "<url> [--dark] [--out <dir>]"
5
+ allowed-tools: Bash, Read, Write, Glob
6
+ ---
7
+
8
+ # Extract Design Language
9
+
10
+ Extract the complete design language from any website URL.
11
+
12
+ ## Process
13
+
14
+ 1. **Run the extraction CLI** on the provided URL:
15
+
16
+ ```bash
17
+ cd "${CLAUDE_SKILL_DIR}/../.." && node bin/design-extract.js $ARGUMENTS
18
+ ```
19
+
20
+ If dependencies are not installed, run first:
21
+ ```bash
22
+ cd "${CLAUDE_SKILL_DIR}/../.." && npm install
23
+ ```
24
+
25
+ 2. **Read the generated markdown file** to understand the design:
26
+
27
+ ```bash
28
+ cat design-extract-output/*-design-language.md
29
+ ```
30
+
31
+ 3. **Present key findings** to the user:
32
+ - Primary color palette (with hex codes)
33
+ - Font families in use
34
+ - Spacing system (base unit if detected)
35
+ - Number of component patterns found
36
+ - Any notable design decisions (shadows, border-radius scale, etc.)
37
+
38
+ 4. **Offer next steps:**
39
+ - Copy `tailwind.config.js` into the user's project
40
+ - Import `variables.css` into their stylesheet
41
+ - Use `design-tokens.json` for tooling integration
42
+ - Use the markdown file as a reference for AI-assisted development
43
+
44
+ ## Output Files
45
+
46
+ The tool generates 4 files in the output directory:
47
+
48
+ | File | Purpose |
49
+ |------|---------|
50
+ | `*-design-language.md` | AI-optimized markdown describing the full design system |
51
+ | `*-design-tokens.json` | W3C Design Tokens format for tooling |
52
+ | `*-tailwind.config.js` | Ready-to-use Tailwind CSS theme extension |
53
+ | `*-variables.css` | CSS custom properties for direct use |
54
+
55
+ ## Options
56
+
57
+ - `--out <dir>` — Output directory (default: `./design-extract-output`)
58
+ - `--dark` — Also extract dark mode color scheme
59
+ - `--wait <ms>` — Wait time after page load for SPAs
package/src/crawler.js ADDED
@@ -0,0 +1,162 @@
1
+ import { chromium } from 'playwright';
2
+
3
+ const MAX_ELEMENTS = 5000;
4
+
5
+ export async function crawlPage(url, options = {}) {
6
+ const { width = 1280, height = 800, wait = 0, dark = false } = options;
7
+
8
+ const browser = await chromium.launch({ headless: true });
9
+ const context = await browser.newContext({
10
+ viewport: { width, height },
11
+ colorScheme: 'light',
12
+ });
13
+ const page = await context.newPage();
14
+
15
+ await page.goto(url, { waitUntil: 'networkidle', timeout: 30000 });
16
+ if (wait > 0) await page.waitForTimeout(wait);
17
+
18
+ // Wait for fonts to load
19
+ await page.evaluate(() => document.fonts.ready);
20
+
21
+ const lightData = await extractPageData(page);
22
+
23
+ let darkData = null;
24
+ if (dark) {
25
+ await context.close();
26
+ const darkContext = await browser.newContext({
27
+ viewport: { width, height },
28
+ colorScheme: 'dark',
29
+ });
30
+ const darkPage = await darkContext.newPage();
31
+ await darkPage.goto(url, { waitUntil: 'networkidle', timeout: 30000 });
32
+ await darkPage.evaluate(() => document.fonts.ready);
33
+ darkData = await extractPageData(darkPage);
34
+ await darkContext.close();
35
+ }
36
+
37
+ const title = await page.title();
38
+ await browser.close();
39
+
40
+ return { url, title, light: lightData, dark: darkData };
41
+ }
42
+
43
+ async function extractPageData(page) {
44
+ return page.evaluate((maxElements) => {
45
+ const results = {
46
+ computedStyles: [],
47
+ cssVariables: {},
48
+ mediaQueries: [],
49
+ keyframes: [],
50
+ };
51
+
52
+ // 1. Walk all elements and collect computed styles
53
+ const allElements = document.querySelectorAll('*');
54
+ const elements = allElements.length > maxElements
55
+ ? Array.from(allElements).slice(0, maxElements)
56
+ : Array.from(allElements);
57
+
58
+ for (const el of elements) {
59
+ const cs = getComputedStyle(el);
60
+ const tag = el.tagName.toLowerCase();
61
+ const classList = Array.from(el.classList).join(' ');
62
+ const role = el.getAttribute('role') || '';
63
+
64
+ // Get bounding rect for area estimation
65
+ const rect = el.getBoundingClientRect();
66
+ const area = rect.width * rect.height;
67
+
68
+ results.computedStyles.push({
69
+ tag,
70
+ classList,
71
+ role,
72
+ area,
73
+ color: cs.color,
74
+ backgroundColor: cs.backgroundColor,
75
+ backgroundImage: cs.backgroundImage,
76
+ borderColor: cs.borderColor,
77
+ fontFamily: cs.fontFamily,
78
+ fontSize: cs.fontSize,
79
+ fontWeight: cs.fontWeight,
80
+ lineHeight: cs.lineHeight,
81
+ letterSpacing: cs.letterSpacing,
82
+ paddingTop: cs.paddingTop,
83
+ paddingRight: cs.paddingRight,
84
+ paddingBottom: cs.paddingBottom,
85
+ paddingLeft: cs.paddingLeft,
86
+ marginTop: cs.marginTop,
87
+ marginRight: cs.marginRight,
88
+ marginBottom: cs.marginBottom,
89
+ marginLeft: cs.marginLeft,
90
+ gap: cs.gap,
91
+ borderRadius: cs.borderRadius,
92
+ boxShadow: cs.boxShadow,
93
+ zIndex: cs.zIndex,
94
+ transition: cs.transition,
95
+ animation: cs.animation,
96
+ display: cs.display,
97
+ position: cs.position,
98
+ });
99
+ }
100
+
101
+ // 2. Extract CSS custom properties from :root
102
+ const rootStyles = getComputedStyle(document.documentElement);
103
+ // Get all custom properties by iterating stylesheets
104
+ try {
105
+ for (const sheet of document.styleSheets) {
106
+ try {
107
+ for (const rule of sheet.cssRules) {
108
+ if (rule.selectorText === ':root' || rule.selectorText === ':host') {
109
+ for (let i = 0; i < rule.style.length; i++) {
110
+ const prop = rule.style[i];
111
+ if (prop.startsWith('--')) {
112
+ results.cssVariables[prop] = rule.style.getPropertyValue(prop).trim();
113
+ }
114
+ }
115
+ }
116
+ }
117
+ } catch { /* cross-origin stylesheet, skip */ }
118
+ }
119
+ } catch { /* no stylesheets accessible */ }
120
+
121
+ // Also get any custom properties from the computed style
122
+ // (fallback for CSS-in-JS that sets vars on :root)
123
+ for (let i = 0; i < rootStyles.length; i++) {
124
+ const prop = rootStyles[i];
125
+ if (prop.startsWith('--') && !results.cssVariables[prop]) {
126
+ results.cssVariables[prop] = rootStyles.getPropertyValue(prop).trim();
127
+ }
128
+ }
129
+
130
+ // 3. Extract media queries from stylesheets
131
+ try {
132
+ for (const sheet of document.styleSheets) {
133
+ try {
134
+ for (const rule of sheet.cssRules) {
135
+ if (rule instanceof CSSMediaRule) {
136
+ results.mediaQueries.push(rule.conditionText || rule.media.mediaText);
137
+ }
138
+ }
139
+ } catch { /* cross-origin */ }
140
+ }
141
+ } catch { /* no access */ }
142
+
143
+ // 4. Extract keyframes
144
+ try {
145
+ for (const sheet of document.styleSheets) {
146
+ try {
147
+ for (const rule of sheet.cssRules) {
148
+ if (rule instanceof CSSKeyframesRule) {
149
+ const steps = [];
150
+ for (const kf of rule.cssRules) {
151
+ steps.push({ offset: kf.keyText, style: kf.style.cssText });
152
+ }
153
+ results.keyframes.push({ name: rule.name, steps });
154
+ }
155
+ }
156
+ } catch { /* cross-origin */ }
157
+ }
158
+ } catch { /* no access */ }
159
+
160
+ return results;
161
+ }, MAX_ELEMENTS);
162
+ }
@@ -0,0 +1,28 @@
1
+ export function extractAnimations(computedStyles, keyframes) {
2
+ const transitionSet = new Set();
3
+ const easingSet = new Set();
4
+ const durationSet = new Set();
5
+
6
+ for (const el of computedStyles) {
7
+ if (el.transition && el.transition !== 'all 0s ease 0s' && el.transition !== 'none') {
8
+ transitionSet.add(el.transition);
9
+
10
+ // Extract easing and duration
11
+ const dMatch = el.transition.match(/([\d.]+m?s)/g);
12
+ if (dMatch) dMatch.forEach(d => durationSet.add(d));
13
+
14
+ const eMatch = el.transition.match(/(ease|ease-in|ease-out|ease-in-out|linear|cubic-bezier\([^)]+\))/g);
15
+ if (eMatch) eMatch.forEach(e => easingSet.add(e));
16
+ }
17
+ }
18
+
19
+ return {
20
+ transitions: [...transitionSet],
21
+ easings: [...easingSet],
22
+ durations: [...durationSet],
23
+ keyframes: keyframes.map(kf => ({
24
+ name: kf.name,
25
+ steps: kf.steps,
26
+ })),
27
+ };
28
+ }