Skip to content

Latest commit

 

History

History
473 lines (401 loc) · 13.9 KB

File metadata and controls

473 lines (401 loc) · 13.9 KB

SketchKit — Project Structure

Repository Layout

sketchkit/
│
├── src/
│   ├── base/
│   │   ├── component.js          # Base WireComponent class all components extend
│   │   ├── styles.css             # Core sketchy aesthetic (font, borders, palette)
│   │   └── icons.js               # Simple SVG icon set (search, chevron, check, x, plus, etc.)
│   │
│   ├── components/
│   │   ├── frames/
│   │   │   ├── browser.js
│   │   │   ├── phone.js
│   │   │   ├── tablet.js
│   │   │   ├── desktop-app.js
│   │   │   └── frame.js
│   │   │
│   │   ├── layout/
│   │   │   ├── header.js
│   │   │   ├── footer.js
│   │   │   ├── sidebar.js
│   │   │   ├── main.js
│   │   │   ├── section.js
│   │   │   ├── panel.js
│   │   │   ├── columns.js
│   │   │   ├── split.js
│   │   │   ├── divider.js
│   │   │   └── spacer.js
│   │   │
│   │   ├── navigation/
│   │   │   ├── navbar.js
│   │   │   ├── nav-item.js
│   │   │   ├── breadcrumb.js      # <w-breadcrumb> and <w-crumb>
│   │   │   ├── tabs.js            # <w-tabs> and <w-tab>
│   │   │   ├── menu.js            # <w-menu> and <w-menu-item>
│   │   │   ├── pagination.js
│   │   │   └── steps.js           # <w-steps> and <w-step>
│   │   │
│   │   ├── typography/
│   │   │   ├── heading.js
│   │   │   ├── paragraph.js
│   │   │   ├── label.js
│   │   │   ├── link.js
│   │   │   ├── blockquote.js
│   │   │   ├── code.js
│   │   │   ├── badge.js
│   │   │   ├── lorem.js
│   │   │   └── annotation.js
│   │   │
│   │   ├── forms/
│   │   │   ├── input.js
│   │   │   ├── textarea.js
│   │   │   ├── select.js
│   │   │   ├── checkbox.js        # <w-checkbox> and <w-checkbox-group>
│   │   │   ├── radio.js           # <w-radio> and <w-radio-group>
│   │   │   ├── toggle.js
│   │   │   ├── slider.js
│   │   │   ├── date-input.js
│   │   │   ├── time-input.js
│   │   │   ├── file-upload.js
│   │   │   ├── search.js
│   │   │   ├── stepper.js
│   │   │   ├── color-picker.js
│   │   │   ├── form.js
│   │   │   └── form-actions.js
│   │   │
│   │   ├── buttons/
│   │   │   ├── button.js
│   │   │   ├── button-group.js
│   │   │   ├── icon-button.js
│   │   │   ├── link-button.js
│   │   │   └── fab.js
│   │   │
│   │   ├── data/
│   │   │   ├── table.js           # <w-table>, <w-row>, <w-cell>
│   │   │   ├── list.js            # <w-list>, <w-list-item>
│   │   │   ├── card.js
│   │   │   ├── stat.js
│   │   │   ├── avatar.js
│   │   │   ├── image.js
│   │   │   ├── video.js
│   │   │   ├── map.js
│   │   │   ├── chart.js
│   │   │   ├── tag-list.js
│   │   │   ├── tree.js            # <w-tree>, <w-tree-node>
│   │   │   └── timeline.js        # <w-timeline>, <w-timeline-entry>
│   │   │
│   │   ├── feedback/
│   │   │   ├── alert.js
│   │   │   ├── toast.js
│   │   │   ├── progress.js
│   │   │   ├── spinner.js
│   │   │   ├── skeleton.js
│   │   │   ├── empty-state.js
│   │   │   └── tooltip.js
│   │   │
│   │   ├── overlays/
│   │   │   ├── modal.js
│   │   │   ├── drawer.js
│   │   │   ├── popover.js
│   │   │   ├── dropdown.js
│   │   │   └── context-menu.js
│   │   │
│   │   ├── media/
│   │   │   ├── carousel.js
│   │   │   ├── accordion.js       # <w-accordion>, <w-accordion-item>
│   │   │   ├── embed.js
│   │   │   ├── audio.js
│   │   │   ├── video-player.js
│   │   │   ├── rich-editor.js
│   │   │   └── comment.js         # <w-comment>, <w-thread>
│   │   │
│   │   └── specialized/
│   │       ├── login.js
│   │       ├── signup.js
│   │       ├── pricing.js
│   │       ├── calendar.js
│   │       ├── rating.js
│   │       ├── filter-bar.js
│   │       ├── sort.js
│   │       ├── scrollbar.js
│   │       ├── ad.js
│   │       └── cookie-banner.js
│   │
│   └── manifest.txt               # Ordered list of files for concatenation
│
├── build.sh                        # Shell script: cats files in manifest order → dist/sketchkit.js
├── preview.sh                      # Playwright screenshot helper for agent self-review
│
├── dist/
│   ├── sketchkit.js                # Concatenated bundle — all components + styles
│   └── template.html               # Starter wireframe template with sketchkit.js inlined
│
├── catalog/
│   └── index.html                  # Visual catalog — every component rendered on one page
│
├── examples/
│   ├── dashboard.html
│   ├── login-flow.html
│   ├── settings.html
│   ├── ecommerce.html
│   ├── mobile-feed.html
│   ├── email-client.html
│   ├── landing-page.html
│   ├── contact-center.html
│   ├── data-table.html
│   └── wizard-flow.html
│
├── ai/
│   ├── COMPONENTS.md               # AI-readable component reference
│   ├── SKILL.md                    # Claude Code skill file
│   ├── system-prompt.md            # Standalone system prompt for non-Claude-Code usage
│   └── examples/
│       ├── 01-simple-form.md
│       ├── 02-dashboard.md
│       ├── 03-mobile-screen.md
│       ├── 04-iteration.md
│       └── 05-multi-screen.md
│
├── README.md
├── LICENSE
└── .gitignore

Build

A shell script that concatenates source files into one bundle. No Node, no npm, no dependencies.

src/manifest.txt

Ordered list of source files. Base class and styles first, then all components in any order (nothing extends anything but the base class).

base/styles.css
base/component.js
base/icons.js
components/frames/browser.js
components/frames/phone.js
components/frames/tablet.js
...

build.sh

#!/bin/bash
set -e

OUT="dist/sketchkit.js"
CSS_FILE="src/base/styles.css"

mkdir -p dist

# Start with CSS injected as JS
echo "/* SketchKit — Lo-Fi Wireframe Components */" > "$OUT"
echo "(function() {" >> "$OUT"
echo "const __css = \`" >> "$OUT"
cat "$CSS_FILE" >> "$OUT"
echo "\`;" >> "$OUT"
echo "const __style = document.createElement('style');" >> "$OUT"
echo "__style.textContent = __css;" >> "$OUT"
echo "document.head.appendChild(__style);" >> "$OUT"

# Concatenate all JS files from manifest (skip CSS entries)
while IFS= read -r file; do
  [[ "$file" == *.css ]] && continue
  [[ "$file" == \#* ]] && continue
  [[ -z "$file" ]] && continue
  echo "" >> "$OUT"
  echo "// --- $file ---" >> "$OUT"
  cat "src/$file" >> "$OUT"
done < src/manifest.txt

echo "})();" >> "$OUT"

echo "Built $OUT ($(wc -c < "$OUT") bytes)"

Run ./build.sh. That's the entire build system.


Component Pattern

No shadow DOM. Components render light DOM children and use shared CSS classes from styles.css. Everything lives in one document context.

Base class

class WireComponent extends HTMLElement {
  connectedCallback() {
    // Capture original innerHTML before first render (for components with child content)
    if (this._slotContent === undefined) {
      this._slotContent = this.innerHTML;
    }
    this._render();
  }

  static get observedAttributes() { return []; }

  attributeChangedCallback() {
    if (this.isConnected) this._render();
  }

  _render() {
    const content = this.render();
    if (content !== undefined) {
      this.innerHTML = content;
    }
  }

  render() {}

  attr(name, fallback = '') {
    return this.getAttribute(name) ?? fallback;
  }

  hasAttr(name) {
    return this.hasAttribute(name);
  }
}

Leaf component (no child content)

class WInput extends WireComponent {
  static get observedAttributes() {
    return ['label', 'placeholder', 'type', 'disabled'];
  }

  render() {
    const label = this.attr('label');
    const placeholder = this.attr('placeholder', 'Type here...');
    return `
      ${label ? `<div class="w-label">${label}</div>` : ''}
      <div class="w-input-box">${placeholder}</div>
    `;
  }
}
customElements.define('w-input', WInput);

Container component (wraps child content)

class WCard extends WireComponent {
  render() {
    const title = this.attr('title');
    return `
      <div class="w-card">
        ${title ? `<div class="w-card-header">${title}</div>` : ''}
        <div class="w-card-body">${this._slotContent}</div>
      </div>
    `;
  }
}
customElements.define('w-card', WCard);

_slotContent is captured once on first connectedCallback, so re-renders from attribute changes don't destroy nested content.


Style Strategy

All styles live in src/base/styles.css, injected once into <head> by the bundle. Two concerns, cleanly separated:

Sketchy component styles — the w- prefixed classes that give components their hand-drawn look:

:root {
  --w-bg: #ffffff;
  --w-bg-muted: #f3f3f3;
  --w-border: #333333;
  --w-text: #222222;
  --w-text-muted: #888888;
  --w-accent: #4a90d9;
}

.w-box {
  border: 2px solid var(--w-border);
  border-radius: 3px 4px 3px 5px;
}

.w-input-box {
  border: 2px solid var(--w-border);
  border-radius: 4px 3px 5px 3px;
  padding: 6px 10px;
  color: var(--w-text-muted);
  background: var(--w-bg);
}

.w-label {
  font-size: 14px;
  color: var(--w-text);
  margin-bottom: 4px;
}

.w-card { ... }
.w-card-header { ... }
.w-navbar { ... }
/* etc. */

Tailwind for layout — applied by the AI on <div> wrappers in the wireframe HTML:

<div class="flex gap-4 p-6">
  <w-sidebar class="w-64">...</w-sidebar>
  <w-main class="flex-1">...</w-main>
</div>

Components don't position themselves. Tailwind handles all spacing, sizing, and arrangement. The two systems don't overlap.


AI Output Format

The AI generates a single self-contained HTML file with the entire sketchkit.js inlined. Two CDN dependencies (Tailwind, Google Fonts) require internet on first load but cache aggressively.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Wireframe — [Name]</title>
  <script src="https://cdn.tailwindcss.com"></script>
  <script>
    tailwind.config = {
      theme: {
        extend: {
          fontFamily: { sketch: ['"Patrick Hand"', 'cursive'] }
        }
      }
    }
  </script>
  <link href="https://fonts.googleapis.com/css2?family=Patrick+Hand&display=swap"
        rel="stylesheet">
  <script>
  // --- SketchKit v0.1.0 ---
  // [Entire sketchkit.js inlined here]
  </script>
</head>
<body class="font-sketch bg-white text-gray-800 p-4">

  <!-- AI-generated wireframe content -->

</body>
</html>

AI Integration Files

ai/COMPONENTS.md

Concise reference for AI consumption. One block per component: tag, attributes table, one example. Target: under 4K tokens so it fits in context alongside the user's wireframe request.

## <w-input>
Text input field.
| Attr | Values | Default |
|------|--------|---------|
| label | string ||
| placeholder | string | "Type here..." |
| type | text / email / password | text |
| disabled | flag ||

​```html
<w-input label="Email" placeholder="you@example.com" type="email"></w-input>
​```

ai/SKILL.md

Constraint layer for Claude Code:

# SketchKit Wireframe Generation

Generate lo-fi wireframes using SketchKit Web Components.

## Rules
1. ONLY use <w-*> components from COMPONENTS.md — no raw HTML
   except <div> for Tailwind layout wrappers
2. Use Tailwind classes for all layout: flex, grid, gap, p-*, m-*, w-*, h-*
3. Output a single self-contained .html file using the standard template
4. Grayscale only — no custom colors beyond the built-in palette
5. One screen per file
6. Components are static — no interactivity

## Workflow
1. Read COMPONENTS.md for available components
2. Identify which components match the user's description
3. Plan layout: frame → regions → content
4. Generate the HTML
5. Save to output

ai/examples/

Prompt-and-output pairs for few-shot learning. Each shows a user request and the complete HTML wireframe, demonstrating component selection and layout patterns.


Development Workflow

  1. Edit a component in src/components/{category}/{name}.js
  2. Run ./build.sh
  3. Open catalog/index.html to see all components rendered
  4. If you added or changed attributes, update ai/COMPONENTS.md
  5. Test with AI: give Claude Code the skill file and ask for a wireframe

Intentional Exclusions

  • No shadow DOM. One document, shared styles, simple.
  • No build dependencies. Shell script concatenation. No Node required.
  • No interactivity. Static wireframe representations only. Modals render open, toggles render in one state, tabs show the active state.
  • No responsive design. Wireframes target a specific viewport. Choose the right page frame.
  • No theming. One look: sketchy grayscale. The constraint is the feature.
  • No package manager. Clone the repo, run build.sh.