From shipshitdev-library
Generates minimal semantic HTML pages for browser review of agent output like lists/tables/drafts hard to read in terminal. Auto-activates on 'show me', 'quick view', 'make reviewable'. Manages _private/views/ files: temp → keeper → archived.
npx claudepluginhub shipshitdev/skillsThis skill uses the workspace's default tool permissions.
Generate minimal HTML to review structured data in a browser. Minimal styling, maximum readability.
Provides UI/UX resources: 50+ styles, color palettes, font pairings, guidelines, charts for web/mobile across React, Next.js, Vue, Svelte, Tailwind, React Native, Flutter. Aids planning, building, reviewing interfaces.
Fetches up-to-date documentation from Context7 for libraries and frameworks like React, Next.js, Prisma. Use for setup questions, API references, and code examples.
Analyzes competition with Porter's Five Forces, Blue Ocean Strategy, and positioning maps to identify differentiation opportunities and market positioning for startups and pitches.
Generate minimal HTML to review structured data in a browser. Minimal styling, maximum readability.
DO:
<table>, <ul>, <details>, <pre>, <h1-3>_private/views/open _private/views/{filename}DO NOT:
Views have a lifecycle: temporary → keeper → archived.
| Stage | Filename | When |
|---|---|---|
| Temporary | name-temp.html | Default for new views |
| Keeper | name.html | User says "keep this", "this is good" |
| Archived | name.2025-01-01.html | Previous keeper when promoting new one |
Rules:
-temp suffix — Every new view starts as name-temp.htmlname.htmlname.html exists, rename to name.DATE.html before promoting-temp filesWorkflow:
# First iteration
drafts-temp.html ← created
# User: "keep this"
drafts.html ← promoted (temp deleted)
# Later iteration
drafts-temp.html ← new temp created
drafts.html ← keeper untouched
# User: "this is better, keep it"
drafts.2025-01-01.html ← old keeper archived
drafts.html ← new keeper promoted
Trigger phrases for promotion:
Every quick-view HTML file:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{title}</title>
<style>
:root {
--bg: #fff;
--text: #222;
--muted: #666;
--border: #ddd;
--accent: #1976d2;
}
@media (prefers-color-scheme: dark) {
:root {
--bg: #1a1a1a;
--text: #e0e0e0;
--muted: #999;
--border: #333;
--accent: #64b5f6;
}
}
body {
max-width: 800px;
margin: 40px auto;
padding: 0 20px;
font-family: system-ui;
background: var(--bg);
color: var(--text);
}
table { border-collapse: collapse; width: 100%; }
td, th { border: 1px solid var(--border); padding: 8px; text-align: left; }
.meta { color: var(--muted); font-size: 0.875rem; margin-bottom: 1rem; }
details { margin: 0.5rem 0; }
summary { cursor: pointer; }
pre {
background: var(--border);
padding: 1rem;
overflow-x: auto;
border-radius: 4px;
}
/* Long content truncation */
.truncate {
max-height: 200px;
overflow: hidden;
position: relative;
}
.truncate.expanded { max-height: none; }
.truncate:not(.expanded)::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 60px;
background: linear-gradient(transparent, var(--bg));
}
.expand-btn {
color: var(--accent);
background: none;
border: none;
cursor: pointer;
padding: 0.5rem 0;
font-size: 0.875rem;
}
/* Type borders */
.type-user { border-left: 3px solid var(--accent); padding-left: 1rem; }
.type-draft { border-left: 3px solid #ff9800; padding-left: 1rem; }
.type-done { border-left: 3px solid #4caf50; padding-left: 1rem; }
/* Source attribution */
.source { color: var(--muted); font-size: 0.75rem; }
.source a { color: var(--muted); }
.source a:hover { color: var(--accent); }
</style>
</head>
<body>
<p class="meta">Generated: {timestamp} · {count} items</p>
{content}
<script>
// Truncation toggle
document.querySelectorAll('.truncate').forEach(el => {
if (el.scrollHeight > 220) {
const btn = document.createElement('button');
btn.className = 'expand-btn';
btn.textContent = 'Show more';
btn.onclick = () => {
el.classList.toggle('expanded');
btn.textContent = el.classList.contains('expanded') ? 'Show less' : 'Show more';
};
el.after(btn);
} else {
el.classList.add('expanded'); // No truncation needed
}
});
</script>
</body>
</html>
<h1>Title</h1>
<ul>
<li><strong>@username</strong> — action item</li>
</ul>
<table>
<tr><th>Contact</th><th>Action</th><th>Draft</th></tr>
<tr><td>@name</td><td>Follow up</td><td>Hey...</td></tr>
</table>
<details>
<summary><strong>@username</strong> — action</summary>
<div class="truncate">
<pre>Long content here that may need truncation...</pre>
</div>
</details>
<div class="type-user">User message or input</div>
<div class="type-draft">Draft content</div>
<div class="type-done">Completed item</div>
<p>
<a href="tg://resolve?domain=username">Open Telegram</a> ·
<button onclick="navigator.clipboard.writeText('draft text')">Copy</button>
</p>
When displaying data gathered from external sources, always include attribution links for drill-down.
Add to base template CSS:
.source { color: var(--muted); font-size: 0.75rem; }
.source a { color: var(--muted); }
.source a:hover { color: var(--accent); }
Inline attribution (preferred for lists):
<div class="tip">
<strong>Tip title</strong> — Description of the tip.
<span class="source">— <a href="https://x.com/user/status/123">@username</a></span>
</div>
Table with source column:
<table>
<tr><th>Tip</th><th>Source</th></tr>
<tr>
<td>Description here</td>
<td class="source"><a href="https://x.com/user/status/123">@user</a></td>
</tr>
</table>
Expandable with source in summary:
<details>
<summary><strong>Tip title</strong> <span class="source">— <a href="URL">@source</a></span></summary>
<p>Full content...</p>
</details>
Meta header with main source:
<p class="meta">
Generated: {timestamp} · {count} items ·
Source: <a href="https://x.com/user/status/123">Original thread</a>
</p>
Principles:
@username for social media, domain for articlesFor drafts that user may edit before sending. Tracks original vs edited for later analysis.
<details>
<summary><strong>@username</strong> — action <span class="status"></span></summary>
<pre contenteditable="true"
data-username="username"
data-original="Original draft text here"
onblur="saveDraft(this)">Original draft text here</pre>
<div class="actions">
<a href="tg://resolve?domain=username">Open Telegram</a>
<button onclick="copyDraft(this)">Copy</button>
</div>
</details>
Include this script block at end of <body> (before closing </body> tag):
function saveDraft(el) {
const key = 'draft_' + el.dataset.username;
const edited = el.textContent.trim();
const original = el.dataset.original;
if (edited !== original) {
localStorage.setItem(key, edited);
el.closest('details').querySelector('.status').textContent = '(edited)';
}
}
function copyDraft(btn) {
const pre = btn.closest('details').querySelector('pre');
navigator.clipboard.writeText(pre.textContent.trim());
btn.textContent = 'Copied!';
setTimeout(() => btn.textContent = 'Copy', 1500);
}
function restoreEdits() {
document.querySelectorAll('pre[data-username]').forEach(el => {
const saved = localStorage.getItem('draft_' + el.dataset.username);
if (saved) {
el.textContent = saved;
el.closest('details').querySelector('.status').textContent = '(edited)';
}
});
}
function exportEdits() {
const edits = [];
document.querySelectorAll('pre[data-username]').forEach(el => {
const original = el.dataset.original;
const current = el.textContent.trim();
if (original !== current) {
edits.push({ username: el.dataset.username, original, edited: current });
}
});
if (edits.length === 0) { alert('No edits to export'); return; }
const blob = new Blob([JSON.stringify({exported_at: new Date().toISOString(), edits}, null, 2)], {type: 'application/json'});
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = 'draft_edits.json';
a.click();
}
restoreEdits();
Add export button in header when using editable drafts:
<p class="meta">Generated: {timestamp} · {count} drafts · <button onclick="exportEdits()">Export Edits</button></p>
_private/views/{name}-temp.htmlopen _private/views/{name}-temp.html{name}.htmlUser: "show me the drafts"
Agent:
_private/drafts/outreach_drafts.md<details> for each draft_private/views/drafts-temp.htmlopen _private/views/drafts-temp.htmlResult: Browser opens, user sees expandable list of drafts with auto dark/light mode, long content truncated with "Show more", can copy each one.
User: "this looks good, keep it"
Agent:
drafts-temp.html → drafts.htmlThis skill produces functional HTML with minimal styling. For full visual styling, invoke the html-style skill after generating.
Classes used by quick-view (compatible with html-style):
| Class | Purpose |
|---|---|
.type-user | User input/message |
.type-draft | Draft content |
.type-done | Completed item |
.source | Attribution links |
.meta | Metadata header |
.truncate | Long content container |
.actions | Action button container |
Data attributes for JS hooks:
data-username — Identifier for draftsdata-original — Original text for diff trackingTruncation pattern and CSS variables approach inspired by simon willison's claude-code-transcripts.