Skip to content

Repository files navigation

skelton-auto

A TypeScript-based CLI tool that statically analyzes React/Next.js projects using AST parsing to automatically generate skeleton (loading) placeholder components that mirror the original UI's layout and styling.

skelton-auto demo

Features

  • 🔍 Static Analysis - Uses AST parsing to analyze React/Next.js components
  • 🎨 Layout Preservation - Maintains original component structure (flex, grid, padding, margin, etc.)
  • Data-Fetch Detection - Identifies components with data fetching patterns
  • 🎭 Multiple Styling Systems - Supports Tailwind CSS, CSS Modules, Styled Components, Emotion
  • 🔌 Plugin Architecture - Extensible styling adapter system
  • 📦 Zero Runtime Dependencies - Generated skeletons are pure React components

Installation

# Clone the repository
git clone https://github.com/minaa66/skelton-auto.git
cd skelton-auto

# Install dependencies
npm install

# Build all packages
npm run build

# Link the CLI globally
npm link

Quick Start

# Basic usage - scan and generate skeleton files
skelton-auto ./my-next-app

# Preview changes without writing files
skelton-auto ./my-next-app --dry-run

# Specify styling system
skelton-auto ./my-next-app --style tailwind

# Generate AND integrate into original components
skelton-auto ./my-next-app --integrate

# Custom output directory
skelton-auto ./my-next-app --output ./src/skeletons

CLI Commands

skelton-auto [project-path]

Analyze a project and generate skeleton components.

Options:

Option Alias Default Description
--style -s tailwind Styling system (tailwind, css-modules, styled-components, emotion)
--output -o (same as source) Output directory for skeleton files
--integrate -i false Inject skeleton usage into original components
--config -c skelton.config.js Path to config file
--include **/*.tsx,**/*.jsx Glob patterns for files to include
--exclude node_modules/** Glob patterns for files to exclude
--dry-run false Preview changes without writing files
--init false Create a default config file

skelton-auto init [path]

Create a default configuration file.

skelton-auto analyze [project-path]

Analyze components without generating skeletons.

Configuration

Create a skelton.config.js file in your project root:

// skelton.config.js
module.exports = {
  // Styling system to use
  style: 'tailwind',
  
  // Output directory for skeleton files (optional)
  outputDir: './src/skeletons',
  
  // Whether to inject skeleton usage into original components
  integrate: false,
  
  // Glob patterns for files to include
  include: ['src/**/*.tsx', 'src/**/*.jsx'],
  
  // Glob patterns for files to exclude
  exclude: [
    '**/*.test.*',
    '**/*.stories.*',
    '**/Skeleton.tsx',
    'node_modules/**'
  ],
  
  // Suffix for generated skeleton component names
  skeletonSuffix: 'Skeleton',
  
  // Animation class for placeholders (Tailwind)
  animationClass: 'animate-pulse'
};

How It Works

1. File Scanning

The tool scans your project for React component files (.tsx, .jsx) using fast-glob, respecting include/exclude patterns.

2. AST Parsing

Each file is parsed into an Abstract Syntax Tree (AST) using @babel/parser, supporting TypeScript and JSX syntax.

3. Component Analysis

The analyzer:

  • Identifies React component declarations (function declarations, arrow functions)
  • Detects data-fetching patterns:
    • useEffect
    • useSWR
    • useQuery (React Query/Apollo)
    • fetch
    • axios
    • getServerSideProps
    • getStaticProps
  • Extracts the visual structure from JSX return statements
  • Captures props and their types

4. Skeleton Generation

For each candidate component:

  • Preserves layout classes (flex, grid, padding, margin, width, height)
  • Replaces text nodes with animated placeholder divs
  • Replaces images with placeholder boxes
  • Maintains the original component hierarchy

5. Output

Generates skeleton component files:

  • Named exports for easy imports
  • TypeScript compatible
  • Ready to use in your application

Example

Input Component

// src/components/UserCard.tsx
import { useEffect, useState } from 'react'

export default function UserCard({ userId }: { userId: string }) {
  const [user, setUser] = useState(null)

  useEffect(() => {
    fetch(`/api/users/${userId}`).then(r => r.json()).then(setUser)
  }, [userId])

  return (
    <div className="flex items-center gap-4 p-4 rounded-lg shadow">
      <img className="w-12 h-12 rounded-full" src={user?.avatar} alt={user?.name} />
      <div className="flex flex-col gap-1">
        <span className="text-lg font-semibold">{user?.name}</span>
        <span className="text-sm text-gray-500">{user?.email}</span>
      </div>
    </div>
  )
}

Generated Skeleton

// src/skeletons/UserCardSkeleton.tsx (auto-generated)
import React from 'react';

export const UserCardSkeleton: React.FC = () => {
  return (
    <div className="flex items-center gap-4 p-4 rounded-lg shadow animate-pulse">
      <div className="w-12 h-12 rounded-full bg-gray-200">
        {/* Image placeholder */}
      </div>
      <div className="flex flex-col gap-1">
        <div className="bg-gray-200 rounded animate-pulse">
          {/* Text placeholder */}
        </div>
        <div className="bg-gray-200 rounded animate-pulse">
          {/* Text placeholder */}
        </div>
      </div>
    </div>
  );
};

export default UserCardSkeleton;

Usage in Your App

import UserCard from './components/UserCard'
import UserCardSkeleton from './skeletons/UserCardSkeleton'

function UserProfile({ userId, isLoading }) {
  if (isLoading) {
    return <UserCardSkeleton />
  }
  return <UserCard userId={userId} />
}

Styling Plugins

Three plugins are available, each targeting a different styling approach. All three generate skeletons that respond to CSS variable-based theming at runtime.

Tailwind CSS (Default)

skelton-auto ./my-app --style tailwind
  • Preserves layout classes (flex, grid, gap-, p-, w-, h-, etc.)
  • Strips visual classes (bg-, text-, font-, hover/focus variants)
  • Placeholder color: bg-[var(--skeleton-bg,#e5e7eb)] — responds to CSS variables
  • Card/container surface: bg-[var(--skeleton-surface,#ffffff)]
  • Animation: animate-pulse
  • No companion files needed — pure Tailwind classes

Inline Styles

skelton-auto ./my-app --style inline
  • For components that use React inline style={{}} props
  • Extracts layout-relevant styles (display, flexDirection, gap, padding, width, height, etc.)
  • Strips visual styles (color, background, fontSize, fontWeight, etc.)
  • Placeholder: backgroundColor: 'var(--skeleton-bg, #e5e7eb)'
  • Injects a <style> tag with keyframe animation and CSS token definitions into the generated file header
  • No external CSS files required

CSS Modules

skelton-auto ./my-app --style css-modules
  • For components using .module.css files
  • Parses the source .module.css and strips visual-only rules, keeping layout/sizing
  • Generates a companion ComponentSkeleton.module.css with cleaned rules
  • Generates a shared skeleton-tokens.css (plain CSS, not a module) for :root token definitions
  • Placeholder classes: skeleton-text, skeleton-image, skeleton-container, skeleton-animate
  • Container backgrounds replaced with var(--skeleton-surface, #ffffff) automatically

Theming

All generated skeletons are driven by four CSS custom properties. Set them on any ancestor element to switch themes at runtime — no regeneration needed.

Variable Default (light) Default (dark) Purpose
--skeleton-bg #e5e7eb #4b5563 Placeholder bar / block color
--skeleton-surface #ffffff #1f2937 Card / container background
--skeleton-shimmer-from 1 1 Pulse animation start opacity
--skeleton-shimmer-to 0.4 0.3 Pulse animation end opacity

Theme Modes

Configure the theme in skelton.config.js:

module.exports = {
  style: 'tailwind',
  theme: {
    mode: 'light',    // 'light' | 'dark' | 'auto' | 'custom'
  }
};
Mode Behavior
light Emits light token defaults in :root
dark Emits dark token defaults in :root
auto Emits light defaults + @media (prefers-color-scheme: dark) override
custom Use your own token values for light and/or dark

Auto Mode (OS preference)

module.exports = {
  style: 'css-modules',
  theme: { mode: 'auto' }
};

Generates:

:root {
  --skeleton-bg: #e5e7eb;
  --skeleton-surface: #ffffff;
  --skeleton-shimmer-from: 1;
  --skeleton-shimmer-to: 0.4;
}

@media (prefers-color-scheme: dark) {
  :root {
    --skeleton-bg: #4b5563;
    --skeleton-surface: #1f2937;
    --skeleton-shimmer-from: 1;
    --skeleton-shimmer-to: 0.3;
  }
}

Custom Tokens

module.exports = {
  style: 'tailwind',
  theme: {
    mode: 'custom',
    tokens: {
      light: {
        bg: '#f0f0f0',
        surface: '#ffffff',
        shimmerFrom: 1,
        shimmerTo: 0.5,
      },
      dark: {
        bg: '#374151',
        surface: '#1f2937',
        shimmerFrom: 1,
        shimmerTo: 0.2,
      },
    },
  },
};

Runtime Toggle (React)

You can switch themes at runtime by setting the CSS variables on a wrapper element — no page reload required:

const THEMES = {
  light: {
    '--skeleton-bg': '#e5e7eb',
    '--skeleton-surface': '#ffffff',
    '--skeleton-shimmer-from': '1',
    '--skeleton-shimmer-to': '0.4',
  },
  dark: {
    '--skeleton-bg': '#4b5563',
    '--skeleton-surface': '#1f2937',
    '--skeleton-shimmer-from': '1',
    '--skeleton-shimmer-to': '0.3',
  },
};

function App() {
  const [theme, setTheme] = useState<'light' | 'dark'>('light');

  return (
    <div style={THEMES[theme] as React.CSSProperties}>
      <button onClick={() => setTheme(t => t === 'light' ? 'dark' : 'light')}>
        Toggle theme
      </button>
      <UserCardSkeleton />
    </div>
  );
}

Layout Class Preservation

The tool preserves these layout-related classes:

Category Prefixes
Flexbox flex, items-, justify-
Grid grid, col-, row-
Gap gap-, space-
Padding p-, px-, py-, pt-, pb-, pl-, pr-
Margin m-, mx-, my-, mt-, mb-, ml-, mr-
Width/Height w-, h-, min-, max-
Border border, rounded
Shadow shadow
Overflow overflow-

API Usage

You can also use skelton-auto programmatically:

import {
  loadConfig,
  runPipeline,
  analyzeFile,
  generateSkeleton
} from 'skelton-auto/core';
import tailwindPlugin from 'skelton-auto/plugin-tailwind';

const config = await loadConfig('./skelton.config.js', {
  projectPath: './my-project'
});

const result = await runPipeline(config, async (style) => {
  return tailwindPlugin;
});

console.log(`Generated ${result.generated.length} skeletons`);

Monorepo Structure

skelton-auto/
├── packages/
│   ├── cli/                  # CLI entry point
│   ├── core/                 # Core functionality
│   │   ├── scanner.ts        # File scanning
│   │   ├── parser.ts         # AST parsing
│   │   ├── analyzer.ts       # Component analysis
│   │   ├── generator.ts      # Skeleton generation
│   │   ├── modifier.ts       # Code injection
│   │   └── config.ts         # Configuration
│   ├── plugin-tailwind/      # Tailwind CSS adapter
│   ├── plugin-css-modules/   # CSS Modules adapter
│   └── examples/             # Sample components
├── pnpm-workspace.yaml
├── turbo.json
└── README.md

Contributing

Contributions are welcome! Please read our contributing guidelines before submitting a PR.

Development Setup

# Clone the repository
git clone https://github.com/minaa66/skelton-auto.git
cd skelton-auto

# Install dependencies
npm install

# Build all packages (excludes example apps)
npm run build

# Link the CLI globally so you can run `skelton-auto` from anywhere
npm link

# Verify the link worked
skelton-auto --version

# Run against the example components (dry run)
npm run test:examples

# Run tests
npm test

Using the Linked CLI in Another Project

After npm link, you can run the CLI directly in any local project:

cd /path/to/your-project
skelton-auto . --style tailwind --dry-run

To unlink when you're done:

npm unlink -g skelton-auto

Running Without Linking

If you prefer not to link globally, run the built CLI directly with Node:

node packages/cli/dist/index.js ./packages/examples --dry-run

License

MIT License - see LICENSE for details.

Acknowledgments

About

it automates your skelton animation creation

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages