stock-html: Understand at a Glance with Hand-Drawn Illustrations
Turn any material into a single-file HTML page: two hand-drawn themes (grid paper / blackboard), fixed-character two-panel comics, and all numbers are independently verifiable. Two usage modes:
- Stock Report (
kind="stock", default): A stock, an earnings report, a merger or major news → a complete illustrated report. - General Learning Page (
kind="general"): Articles, papers, technical documentation, course notes → a main thread + concept dependency chain + comic cards + learning path.
This skill comes with its own toolkit—do not hand-write HTML from scratch:
| File | Purpose |
|---|---|
scripts/sketch.py | Canvas, two-panel comics, ready-made charts, page assembly; save() automatically runs offline layout check |
scripts/blocks.py | Semi-finished building blocks: mainline, moat, long-term comparison chart, three comic strip layouts, stock price scenario table—use when making multiple reports at once |
scripts/verify.py | Renders with headless Chrome, reads the page's built-in self-check results, takes segmented screenshots |
assets/template.html | Page template: two themes, jitter filter, stamp progress bar, built-in self-check script |
examples/boba_merger.py | Complete reference implementation (fictional company stock report)—read this before starting work |
references/stock-report.md | How to write each section of a stock report, data discipline |
references/visual-style.md | Visual specifications, canvas coordinates, rules for hand-written SVG |
references/qa.md | Three-layer self-check, key points for reading the illustrations, fallback when Chrome is unavailable |
Three Non-Negotiables
- Zero-background reader. The first time a term appears, explain it in one sentence and give the English original in parentheses. No "as you know," "obviously," or "it's not hard to understand."
- Conclusion first, then reasoning. The first sentence of every section must be the conclusion, not background setup.
- No numbers from memory. All numbers must be verified and cross-calculated by a script before drawing. Mark estimated values as "schematic" and probabilities as "subjective estimate, not a prediction."
Workflow
- Read all materials—user-provided files, screenshots, links, every single one.
- Online verification—prioritize primary sources (company announcements, 8-K, press releases, financial statements); ground every number you plan to use. See "Data Discipline" in
references/stock-report.md. - Write a build script, put all numbers at the top—define all inputs and calculations as variables, use
assertstatements to cross-balance them (price move decomposition, spread, accretion/dilution, multiple × EPS). Do not draw until everything balances. - Arrange the concept dependency chain (8–13 concepts)—arrange so that "the next concept exists to solve a problem introduced by the previous concept," not in the original order of the material. If you can't say what problem a concept solves from the previous one, you've ordered it wrong.
- Assemble the page—following the pattern in
examples/boba_merger.py:import sys; sys.path.insert(0, "<path-to-this-skill>/scripts") from sketch import * p = Page("Title", "Subtitle") # default kind="stock" p.mainline(...); p.checkup(...); p.section(..., role="moat"); p.chain(...); p.card(...) # … p.save("out.html") # write file + offline pre-check; fix any ERRORs - Real rendering check—run
python3 scripts/verify.py out.html --shots shots/. You must see[CHECK light] PASSand[CHECK dark] PASS, then look at every screenshot (at least two sections for each light and dark theme). Seereferences/qa.mdfor details. - Deliver—if the environment supports Artifact publishing, publish it (then check the console to confirm both themes show PASS); otherwise give the HTML file to the user.
- Reply to the user—first, answer their question directly in 2–3 sentences (e.g., "Has the fundamentals changed?"), then list 5–8 bullet points explaining what's in the page, noting any differences from expectations (unconfirmed dates, back-calculated numbers, font fallbacks), and finally list all sources.
Default Structure for Stock Reports
Every section below is mandatory by default (Page.save() will warn about missing sections). This is the "minimum required," not "all there is": if the material has additional important points (valuation vs peers, management & capital allocation, cash flow & debt, customer concentration…), add more section() calls. Each section body ≤ 4 sentences, primarily visual.
| # | Section | How to Call | Must Answer | Which Chart |
|---|---|---|---|---|
| 1 | Main thread | mainline() | What is this really about? (choose one: main loop / pipeline / core contradiction) | One sentence + one diagram |
| 2 | Health check | checkup() | Has the fundamentals changed? Each line with number and measurement basis | ✓ green check / — orange dash / ✗ red cross |
| 3 | Core competitive moat | section(role="moat") | Which 2–3 things can others truly not copy? What evidence number? Where is the weakness? | Moat diagram + tiles |
| 4 | Concept dependency chain | chain() | Which 8–13 concepts do you need to understand this company / news first? | Snake flowchart |
| 5 | Concept cards | card() × N | For each concept: the problem without it → its solution → what it solves and its cost | Two-panel comic |
| 6 | Technology roadmap | section(role="roadmap") | What's done, what's next, which items are just vision? | roadmap() step chart |
| 7 | Long-term plan | section(role="vision") | Where does management want to go in 3–5 years? At current speed, can they get there? How far off? | Target vs current speed comparison chart |
| 8 | Hidden risks | section(role="risks") | 4–6 things not mentioned in the announcement or bull narrative; what is the early warning number for each? | risk_matrix() + warning table |
| 9 | Key milestones | section(role="timeline") | Which days in the next 6–12 months will produce results? Which dates are unconfirmed? | timeline() + schedule |
| 10 | Price scenarios | section(role="paths") | For 2–3 paths: what must happen, what price range, what subjective probability? | paths_chart() + scenario table |
| 11 | Ruler | section(role="ruler") | 2–3 metrics to measure good vs bad; each solution improves which metric at the cost of which? | ruler_chart() + comparison table |
| 12 | Zero-background version (on demand) | section(role="eli5") | The most counterintuitive mechanism, step-by-step with arithmetic | Waterfall chart + steps + comparison table |
| 13 | Learning path | route() | 3–4 stops: why go, how long, link | Metro map |
| — | Footer | sources() | All sources + "For educational purposes only, not investment advice" | — |
For detailed writing guidelines, common clichés, and anti-patterns for each section, see references/stock-report.md (must-read before writing a stock report).
Structure for General Learning Pages
Page(..., kind="general"): Main thread → Concept dependency chain → Concept cards → Ruler (2–3 metrics to measure this domain, drawn on one chart) → Zero-background version (on demand) → Learning path. No health check, price scenarios, or disclaimer.
Rules That Apply Throughout the Page
- Fixed characters. Use the same set of characters across all illustrations; do not change them between concepts. For stocks, default: A, B, C = three old shareholders, D = one new shareholder; pie = shares, coin = profit. Add a small line below the main thread diagram to explain the characters.
- Scaled-down numbers. Represent 921 million shares as 6 pie slices, $1 billion profit as 10 coins; keep proportions real, label as "schematic values."
- Concept cards only have two lines: "What it solves" and "Cost." Cost must never be left blank or written as "basically no cost"—if you can't find a cost, you haven't thought it through.
- Fixed color semantics (consistent across the entire page): Green = ongoing (business, profit); Orange = new (new event, new shareholder); Blue = stored (shares, cash, value); Red dashed = discarded/released (loss). Stamps use purple.
Hard Requirements (check each before publishing)
- Each comic panel caption ≤ 15 characters (including numbers and symbols; for English pages ≤ 32 characters).
- Text must never overlap borders or other elements.
- Primarily visual; each section body ≤ 4 sentences.
- All diagrams are hand-written inline SVG (generated by
sketch.py). No Chart.js / D3 / Mermaid. - All CSS, JS, and SVG must be inlined in a single HTML file; no external links (including Google Fonts—some Artifact environments' CSP blocks them).
- Text must not enter the jitter filter or be transformed (
Canvas.text()already guarantees this; be careful when hand-writing SVG). - Links in the learning path must be opened and verified with a scraping tool to ensure content matches before writing
verified=True. If blocked or unreachable, use an alternative or omit theurl(the page will show "needs manual search"). Fabricating links is the most serious failure of this skill. - Do not mention the user's holdings or options; analyze only the stock itself; do not give buy/sell instructions.
verify.pymust show PASS for both themes, and you must have reviewed the screenshots.
Toolkit Quick Reference
# Canvas: only use semantic colors ink / ink2 / green / orange / blue / red / purple
c = Canvas(760, 300, "name") # large canvas 760 wide, font size ≥ 12
c.rect / line / arrow / curve / circle / path / poly / bar # fill="green" light, "green!" solid
c.text(x, y, "text", size=14, anchor="middle", color="ink", bold=False)
c.person(cx, top, "A", "blue", mood="happy") # fixed character (approx 30×60)
c.pie(cx, cy, r, ["blue"]*6 + ["orange"]*2, labels) c.coins(x, y, 12, "green", cols=4)
c.shop(cx, base, label="BOBA") c.doc(x, y) c.bubble(x, y, w, h) c.tick / cross / minus
# Two-panel comic: 560×232; left panel center x=140, right panel center x=420; your drawing area y 44–188
k = Comic("Left caption ≤15 chars", "Right caption ≤15 chars")
# Ready-made charts (all return Canvas)
snake(nodes) metro(stations) timeline(events) roadmap(stages) risk_matrix(items)
paths_chart(history, scenarios) ruler_chart(points, xr, yr, iso, gauge) waterfall(steps)
# Page
p = Page(title, subtitle, kind="stock", notice=None)
p.mainline(one_sentence, canvas, kind="core_contradiction", note="Fixed characters:…")
p.checkup([("ok" | "mid" | "bad", "one line with number"), ...])
p.chain([(concept_name, new_problem_it_introduces), ...])
p.card(name, en, definition, comic, solves, cost, stamp="next" | "redo" | "done")
p.section(title, prose, canvas=, table=(headers, rows), steps=[…], tiles=[…], after=, note=, role=)
p.route([Stop(name, why, time, url, link_text, verified=True), ...])
p.sources([(title, url), ...]); p.save("out.html")
For English pages: call set_lang("en") before creating any Comic / Page. For detailed parameter documentation, see docstrings in scripts/sketch.py.
When making multiple reports at once (e.g., the top 10 holdings of an ETF), use the building blocks in scripts/blocks.py. Each report only needs numbers and copy:
from blocks import * # also imports all sketch tools
m = mainline_tug(left_title, [(label, value, is_guide)×3], unit_note, problem, sub_problem, [(power, color)×3], right_title, [(date, price), ...], "−6%")
mo = moat(["① Deepest", "② Second", "③ Third"], "TICK")
vi = vision([(value, label1, label2, color, dashed)×3], gap_between=(1, 2), note="algorithm…")
c1 = C_bars(left_label, right_label, [(label, value, color)], [(label, value, color)], lnote=, rnote=, fmt="%g") # before-after bar chart
c2 = C_talk(left_label, right_label, "A", "bubble text", "problem object", "D", "solution object", "one-line note") # character + object
c3 = C_flow(left_label, right_label, ["box", "box"], ["box", "box", "box"], rnote="one-line note") # process boxes
sc = [scen("path", probability, schematic_EPS, low_multiple, high_multiple, "green"), ...]; expect(sc); scen_table(sc, what_must_happen)
p = Page(..., notice=disclaimer("2026-11-11"))
Charts from building blocks are more "standard parts"; for a single report that demands more polished illustrations, still use Canvas / Comic directly—the two can be mixed.
If the ready-made charts are insufficient, use the basic primitives of Canvas to draw your own—the toolkit is a starting point, not a cage; as long as all text goes through c.text(), the checks still work.
When Python Is Not Available
Copy assets/template.html, replace {{BODY}} with hand-written HTML + inline SVG, and fill the remaining placeholders ({{TITLE}}, {{LANG}}, {{CAPMAX}}, {{PAGE_KEY}}, {{LABELS}}) according to the values produced by Page.html() in sketch.py. Hand-written SVG must follow the canvas coordinates and class conventions in references/visual-style.md (svg.chk, rect.panel, text.cap), otherwise the built-in self-check will not recognize them. Open page.html?check=1 in a browser; the bottom check bar must be green PASS.
Common Failure Modes
- ❌ Concepts arranged in original order → dependency chain has no information value; reorder
- ❌ "Cost" written as "virtually no cost" → not thought through; reconsider
- ❌ Characters changed between concepts in different diagrams → readers have to re-identify people; keep them consistent
- ❌ Core competitive moat is just adjectives ("strong brand," "leading technology") without numbers and measurement basis → replace with evidence
- ❌ Treating management's vision as performance guidance and writing it into the long-term plan → clearly label it as a target and cross-check against current speed
- ❌ Hidden risks written as clichés like "macro uncertainty," "increased competition" → write the specific transmission path and the number to watch
- ❌ Writing unannounced dates as fixed → write "expected early December (last year was Dec 2, subject to company announcement)"
- ❌ Probabilities and price levels not marked as "subjective estimate / schematic" → readers will take them as predictions
- ❌ Believing secondary media reports that say "no new news" without checking company filings and SEC documents
- ❌ Not opening learning path links for verification → most serious
- ❌ Writing long paragraphs → each section ≤ 4 sentences
- ❌ Not running
verify.pyor not reviewing screenshots before delivery → a page with overlapping text gets sent out