スキル一覧に戻る
abdullah1854

web-artifacts

by abdullah1854

13🍴 1📅 2026年1月22日
GitHubで見るManusで実行

SKILL.md


name: web-artifacts description: Build sophisticated single-file web applications with React and modern tooling. Activates for "web artifact", "React app", "bundle HTML", "single-file app", "standalone app", "interactive demo". allowed-tools: [Read, Write, Bash, Task]

Web Artifacts Skill

When This Skill Activates

  • "Create a web artifact", "build an app"
  • "Single-file React app", "bundle HTML"
  • "Interactive demo", "standalone app"
  • "Share as HTML", "portable web app"

Core Concept

Web artifacts are self-contained HTML files with all JavaScript, CSS, and assets inlined. Perfect for:

  • Sharing interactive demos
  • Portable tools
  • Prototypes
  • Data visualizations

Quick Start (No Build)

For simple artifacts, use inline scripts:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My Artifact</title>
    <!-- React from CDN -->
    <script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
    <!-- Babel for JSX -->
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <!-- Tailwind -->
    <script src="https://cdn.tailwindcss.com"></script>
    <style>
        body { margin: 0; font-family: system-ui, sans-serif; }
    </style>
</head>
<body>
    <div id="root"></div>
    <script type="text/babel">
        function App() {
            const [count, setCount] = React.useState(0);

            return (
                <div className="min-h-screen bg-neutral-50 flex items-center justify-center">
                    <div className="bg-white p-8 rounded-xl shadow-lg text-center">
                        <h1 className="text-2xl font-bold text-neutral-900 mb-4">
                            Counter: {count}
                        </h1>
                        <button
                            onClick={() => setCount(c => c + 1)}
                            className="px-6 py-2 bg-orange-500 text-white rounded-lg
                                       hover:bg-orange-600 transition-colors"
                        >
                            Increment
                        </button>
                    </div>
                </div>
            );
        }

        ReactDOM.createRoot(document.getElementById('root')).render(<App />);
    </script>
</body>
</html>

Full Build Pipeline

For complex artifacts with bundling:

1. Initialize Project

mkdir artifact-project && cd artifact-project

# Create package.json
cat > package.json << 'EOF'
{
  "name": "artifact",
  "private": true,
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "bundle": "vite build && node bundle.js"
  },
  "dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
  },
  "devDependencies": {
    "@types/react": "^18.2.0",
    "@types/react-dom": "^18.2.0",
    "@vitejs/plugin-react": "^4.2.0",
    "autoprefixer": "^10.4.17",
    "postcss": "^8.4.35",
    "tailwindcss": "^3.4.1",
    "typescript": "^5.3.3",
    "vite": "^5.1.0"
  }
}
EOF

npm install

2. Configure Vite

// vite.config.js
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

export default defineConfig({
  plugins: [react()],
  build: {
    rollupOptions: {
      output: {
        inlineDynamicImports: true,
      }
    }
  }
})

3. Bundle Script

// bundle.js
const fs = require('fs');
const path = require('path');

const distDir = './dist';
const html = fs.readFileSync(path.join(distDir, 'index.html'), 'utf-8');

// Inline all scripts and styles
let bundled = html;

// Find and inline JS
const jsMatch = html.match(/src="(\/assets\/[^"]+\.js)"/);
if (jsMatch) {
  const jsContent = fs.readFileSync(path.join(distDir, jsMatch[1]), 'utf-8');
  bundled = bundled.replace(
    `<script type="module" crossorigin src="${jsMatch[1]}"></script>`,
    `<script type="module">${jsContent}</script>`
  );
}

// Find and inline CSS
const cssMatch = html.match(/href="(\/assets\/[^"]+\.css)"/);
if (cssMatch) {
  const cssContent = fs.readFileSync(path.join(distDir, cssMatch[1]), 'utf-8');
  bundled = bundled.replace(
    `<link rel="stylesheet" crossorigin href="${cssMatch[1]}">`,
    `<style>${cssContent}</style>`
  );
}

fs.writeFileSync('./bundle.html', bundled);
console.log('Created bundle.html');

Design Anti-Patterns (Avoid AI Slop)

DON'TDO INSTEAD
Center everythingUse asymmetric layouts
Purple/gradient backgroundsNeutral tones, single accents
Uniform rounded cornersMix sharp and rounded
Inter font everywhereSystem fonts or distinctive choices
Generic card gridsIntentional visual hierarchy
Excessive shadowsSubtle, purposeful depth

Component Patterns

Card with Hover

function Card({ title, description }) {
  return (
    <div className="group bg-white rounded-lg border border-neutral-200 p-6
                    transition-all duration-200 hover:border-neutral-300 hover:shadow-lg">
      <h3 className="text-lg font-semibold text-neutral-900">{title}</h3>
      <p className="mt-2 text-neutral-600">{description}</p>
      <span className="mt-4 inline-block text-orange-500 group-hover:translate-x-1
                       transition-transform">
        Learn more →
      </span>
    </div>
  );
}

Data Table

function DataTable({ data, columns }) {
  return (
    <div className="overflow-hidden rounded-lg border border-neutral-200">
      <table className="w-full">
        <thead className="bg-neutral-50 border-b border-neutral-200">
          <tr>
            {columns.map(col => (
              <th key={col.key} className="px-4 py-3 text-left text-sm font-medium text-neutral-600">
                {col.label}
              </th>
            ))}
          </tr>
        </thead>
        <tbody className="divide-y divide-neutral-100">
          {data.map((row, i) => (
            <tr key={i} className="hover:bg-neutral-50">
              {columns.map(col => (
                <td key={col.key} className="px-4 py-3 text-sm text-neutral-900">
                  {row[col.key]}
                </td>
              ))}
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
}
function Modal({ open, onClose, children }) {
  if (!open) return null;

  return (
    <div className="fixed inset-0 z-50 flex items-center justify-center">
      <div className="absolute inset-0 bg-black/50" onClick={onClose} />
      <div className="relative bg-white rounded-xl p-6 shadow-xl max-w-md w-full mx-4">
        <button
          onClick={onClose}
          className="absolute top-4 right-4 text-neutral-400 hover:text-neutral-600"
        >
          ✕
        </button>
        {children}
      </div>
    </div>
  );
}

State Management

Local State (useState)

const [items, setItems] = React.useState([]);
const [loading, setLoading] = React.useState(false);
const [error, setError] = React.useState(null);

Complex State (useReducer)

const reducer = (state, action) => {
  switch (action.type) {
    case 'ADD': return { ...state, items: [...state.items, action.payload] };
    case 'REMOVE': return { ...state, items: state.items.filter(i => i.id !== action.id) };
    case 'SET_LOADING': return { ...state, loading: action.payload };
    default: return state;
  }
};

const [state, dispatch] = React.useReducer(reducer, { items: [], loading: false });

Persistence (LocalStorage)

function usePersisted(key, defaultValue) {
  const [value, setValue] = React.useState(() => {
    const saved = localStorage.getItem(key);
    return saved ? JSON.parse(saved) : defaultValue;
  });

  React.useEffect(() => {
    localStorage.setItem(key, JSON.stringify(value));
  }, [key, value]);

  return [value, setValue];
}

// Usage
const [settings, setSettings] = usePersisted('settings', { theme: 'light' });

Output Format

## Web Artifact: [Name]

### Type
[Counter/Form/Dashboard/Tool/Visualization]

### Features
- [Feature 1]
- [Feature 2]

### Build
[No build (CDN) | Vite | Other]

### Output
[Path to HTML file]

### Usage
Open in browser, no server required.

スコア

総合スコア

60/100

リポジトリの品質指標に基づく評価

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

+10
説明文

100文字以上の説明がある

0/10
人気

GitHub Stars 100以上

0/15
最近の活動

3ヶ月以内に更新がある

0/10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

0/5

レビュー

💬

レビュー機能は近日公開予定です