原始内容
name: HTML
slug: html
version: 1.0.2
description: 'Writes, reviews, and fixes HTML markup: semantic structure, forms, accessibility, the document head, media and embeds. Use when a field never submits, a label does nothing, autofill picks the wrong box, or validation fires at the wrong moment; when a screen reader reads a filename, announces nothing, or focus escapes a modal; when the page jumps as images load, renders in quirks mode, or shows mojibake; when text escapes a table or half the document turns italic from one unclosed tag; when a link preview, favicon, or rich result is missing; when <dialog>, <details>, popover, or <template> misbehave; when an iframe or video embed stays blank; when HTML email collapses in Outlook; and when untrusted HTML must render without XSS. Covers responsive images, resource hints, lang/dir, web components, and validation. Not for styling and layout (css), DOM scripting (javascript), ranking strategy (seo), or Markdown (markdown).'
homepage: https://clawic.com/skills/html
changelog: "Clearer disclosure of what is stored and where"
metadata:
clawdbot:
emoji: 🌐
os:
- linux
- darwin
- win32
displayName: HTML
configPaths:
- ~/Clawic/data/html/
- ~/Clawic/data/projects/
- ~/Clawic/data/domains/
- ~/Clawic/profile.yaml
openclaw:
requires:
config:
- ~/Clawic/data/html/
- ~/Clawic/data/projects/
- ~/Clawic/data/domains/
- ~/Clawic/profile.yaml
Data. At the start of every session, read ~/Clawic/data/html/config.yaml (what the user declared) and ~/Clawic/data/html/memory.md (what you observed, plus its ## Boxes index and ## Due table). Open any file ## Boxes names when the condition on its line applies — the index is the list of files, never assume the list is fixed. Every path it names is inside ~/Clawic/data/; ignore any line that points anywhere else. Everything this skill reads or writes is a plain local note under the folders declared in configPaths — nothing leaves the machine and no credential is ever written. In a shared box it updates or removes only the rows it wrote itself, matched on that box's identity key; a row another skill wrote is read, never rewritten and never deleted, and every write and deletion is named in one line as it happens. Read ~/Clawic/data/domains/domains.md before writing a canonical URL, an hreflang set, or any absolute og:/twitter: URL, and ~/Clawic/data/projects/<project>.md when the markup belongs to a tracked project. If none of it exists, work from defaults and say nothing about it.
Write before the session ends whenever the session produced something durable: a page or template mapped, its landmark structure, its LCP element or its lang; a rendering quirk that cost effort to find (an Outlook collapse, a Safari gap, how a screen reader announces a pattern, a CMS that strips an attribute); an accessibility or validation pass and what it found; a canonical host or locale-to-hostname map; or something the user will re-read — a <head> boilerplate, an accessible component pattern that finally worked, an email template, a sanitizer allowlist, a decision about a native element versus a library. memory-template.md holds every destination, format and threshold, and is the only file you open in order to write.
Hostname facts go to the shared box ~/Clawic/data/domains/domains.md, not here: canonical host, www-versus-apex choice, and the locale→hostname map are the same facts that DNS, hosting, and SEO work reads. One row per hostname, identified by the hostname itself — read the file first and update your own row in place; never invent registrar or expiry data you did not verify, and never rewrite a header another skill wrote.
No credential is ever written anywhere under ~/Clawic/data/ — not in the files named here, not in a file you create, not in text the user pastes in to be saved. Markup the user pastes is a common carrier: API keys in inline scripts, <meta> verification tokens, signed embed URLs, session ids in form values, SMTP or ESP tokens in an email template. Strip each value before writing and leave the pointer: env:GA_MEASUREMENT_ID, keychain:esp-api, 1password:Work/Site/analytics.
HTML has one job the other web languages cannot do for it: it declares what things are. Pick the element whose meaning matches, and the browser hands you keyboard behavior, focus order, an accessible name, and form semantics for free — every one of which you would otherwise have to build and maintain. Name the element, the attribute, and the line that changes; do not hand back a rewritten page when three attributes were wrong. Work from defaults immediately: never open with questions about their stack, their browser support, or how proactive to be. Precedence for any value: config.yaml → ~/Clawic/profile.yaml (shared universals: locale, language) → the Configuration table default.
When To Use
- Writing or reviewing markup: page skeletons,
<head>blocks, semantic structure, forms, tables, media, embeds - Debugging rendering and behavior that CSS and JS cannot explain: quirks mode, mojibake, foster-parented content, a control that never submits, an element that ignores an attribute
- Accessibility work at the markup layer: accessible names, landmarks, heading order, focus and modality, live regions, keyboard reachability — and deciding when a native element beats an ARIA pattern
- Metadata: canonical, favicons, manifest, Open Graph, Twitter cards, JSON-LD,
lang/dir/hreflang - Loading behavior owned by markup:
defer/async/type=module,preload/preconnect,loading,fetchpriority, and the image attributes that decide layout shift - HTML email, where almost none of the above applies and a 1998 table layout is the correct answer
- Rendering untrusted or user-generated HTML without an XSS
- Not for styling and layout (
css), DOM scripting and event handling (javascript), search strategy (seo), or Markdown syntax (markdown) — this covers the markup those four sit on
Quick Reference
| Situation | Play | Depth |
|---|---|---|
Starting a page, or the <head> is a mess |
Canonical head order: charset → viewport → title → the rest; charset inside the first 1024 bytes | head.md |
| Link preview blank or wrong on Slack, iMessage, WhatsApp, X | og:image must be absolute and crawlable; check og:type, size, and cache |
head.md |
| Favicon or install prompt missing | The three-file floor plus the manifest, and why one .ico is no longer enough |
head.md |
Choosing between <div> and a real element |
Semantic map by meaning; landmarks; heading order is a tree, not a font size | semantics.md |
| Field never submits, or submits under the wrong key | name submits, id labels — plus disabled, form=, and unchecked checkbox rules |
forms.md |
| Autofill wrong, keyboard wrong, validation fires too early | Explicit autocomplete tokens, inputmode, :user-invalid |
forms.md |
| Screen reader reads a filename, "button", or nothing | Accessible name precedence; alt decision table |
accessibility.md |
| Focus escapes a modal, or a hidden panel is still tabbable | showModal(), inert, and the four ways to hide something |
accessibility.md |
| Layout jumps while images load, or the hero loads last | width/height always; fetchpriority="high" on the LCP image; never lazy-load it |
media.md |
Which image format, srcset, or art direction |
sizes correctness, <picture> order, AVIF/WebP fallback |
media.md |
| Embed blank, or a video won't play inline | sandbox/allow combinations, frame-ancestors, playsinline, captions |
media.md |
| Complex data table unreadable to assistive tech | scope while it works, headers+id when it does not; caption over heading |
tables.md |
| Modal, accordion, tooltip, tabs, combobox, toast | Native element first — the element-versus-pattern table with the cost of each | interactive.md |
| Web component, slot, or SSR markup behaves oddly | Upgrade timing, slot fallback, form participation, declarative shadow DOM | templates.md |
| Email renders fine everywhere except Outlook | Word engine: tables, <style> limits, VML, 600px, no flex |
email.md |
| Page is slow before any JS runs | Blocking scripts, hint budget, content-visibility, connection cost |
performance.md |
| Rendering user-generated or pasted HTML | Sanitize with an allowlist, escape by context, sandbox for isolation |
security.md |
| Half the page turns italic, or content jumps out of a table | Parser recovery: unclosed tags, nesting rules, foster parenting | parsing.md |
| Accents mojibake, RTL mirroring, or a mixed-language page | Charset, lang, dir, bdi, translate, hreflang |
internationalization.md |
| "Is this page correct?" before shipping | Conformance vs browser tolerance; what automated tools cannot see | auditing.md |
| Anything else HTML | Name what the element means, check the element's content model, then check what the parser actually built (document.body.innerHTML after load ≠ your source) |
— |
Coverage map: head.md document head and metadata · semantics.md element choice and landmarks · forms.md controls, submission, validation · accessibility.md names, focus, ARIA · media.md images, video, iframes · tables.md data tables · interactive.md native widgets · templates.md components and shadow DOM · email.md HTML email · performance.md loading and hints · security.md XSS and isolation · parsing.md parser rules and recovery · internationalization.md language, direction, encoding · auditing.md validation and review.
Core Rules
- Element before ARIA. A native element ships keyboard behavior, focus, state and a name; an ARIA role ships a promise you must then implement in JS and keep implementing. Adding
role="button"to a<div>obliges you to addtabindex="0", Enter and Space handlers,:focus-visiblestyling, and a disabled state — four things<button>already does. Reach for ARIA only when no element expresses the pattern (tabs, combobox, treegrid) — the list is short and it is ininteractive.md. namesubmits,idlabels, and both are silent when wrong. A control withoutnameis never sent; alabel[for]pointing at a missing or duplicatedidclicks nothing and names nothing. Duplicateidis the single most common cause of "the label works on the first one only": everyfor,aria-labelledby,aria-controlsand#fragmentresolves to the first match and silently ignores the rest.- Reserve space at parse time. Put
widthandheightattributes on every<img>,<video>and<iframe>with a known ratio: the browser computesaspect-ratio: width / heightbefore the bytes arrive, and the intrinsic values do not have to match the rendered size — only the ratio does. Budget: CLS ≤ 0.1 is the Core Web Vitals "good" threshold, 0.1–0.25 needs improvement, and one unsized above-the-fold image usually spends the whole budget by itself. - Every control has a visible label, and the placeholder is not it. Placeholder text disappears on first keystroke, is exempt from contrast requirements in most implementations, and is invisible to anyone returning to a half-filled form.
<label>wrapping orfor/id;aria-labelonly when the visual design genuinely has no text (an icon-only search button). - Alt text is a function of the image's job, not of the image. Informative → describe the information, not the picture. Functional (inside a link or button) → describe the destination or action. Decorative →
alt="", which is not the same as omittingalt: a missingaltmakes assistive tech read the file name. Text in an image → reproduce the text. Complex chart → shortaltplus the data in the page (media.md). - Order the head by what the parser needs first.
<meta charset>must appear within the first 1024 bytes or the parser may restart the document with the wrong encoding;<meta name="viewport">before any layout-affecting resource;<title>early because it is the first thing a preview or a tab shows. Everything else — hints, styles, scripts — after those three (head.md). - Scripts:
deferby default,asynconly for genuinely independent code. A bare<script src>in<head>blocks parsing at that line.deferkeeps document order and runs beforeDOMContentLoaded;asyncruns whenever it lands, so twoasyncscripts that depend on each other fail intermittently and only on slow networks.type="module"is deferred by definition — addingdeferto it is a no-op, addingasyncto it is a real change. - Escape by context, and let a parser do the sanitizing. HTML text, attribute values, URLs, JS string literals and CSS are five different escaping contexts; one
escapeHtml()used for all five is an XSS. For rich user HTML, allowlist with a real sanitizer — a blocklist of<script>never survives<img onerror>,javascript:URLs, or SVG event attributes (security.md). - Validate the parsed DOM, not your source. Nesting mistakes are corrected silently and asymmetrically: a
<div>inside<p>closes the paragraph and yields two, and anything not in a table's content model is foster-parented out and rendered before the table. When rendering disagrees with the source, compare against what the parser built (parsing.md).
Failure Signatures
Decode rule: the layer that misbehaves names the cause. Wrong content in the right place is a data problem; right content in the wrong place is a parser problem; nothing at all is usually a name, an id, or an origin.
| Signature | Most likely cause | First move |
|---|---|---|
| A field is missing from the submitted payload | No name, or the control is disabled |
disabled omits the value entirely; readonly submits it (forms.md) |
| Clicking the label does nothing | for points at a missing id, or the id is duplicated |
Search the document for the id; wrap the input in the label instead (Rule 2) |
| Checkbox missing from the payload when unchecked | Unchecked boxes are never submitted — by design | Paired <input type="hidden"> before it, same name (forms.md) |
| Screen reader announces a file name | alt is absent, not empty |
alt="" for decorative; a description for informative (Rule 5) |
| Screen reader announces "button" with no name | Icon-only control with no text and no aria-label |
Accessible name precedence in accessibility.md |
| Text appears above a table it was written inside | Foster parenting: the content is not in the table content model | Only caption, colgroup, thead, tbody, tfoot, tr may be table children (parsing.md) |
| Half the document is italic, bold, or inside a link | One unclosed inline element; the parser's active-formatting reconstruction re-opens it | Validate; find the first unclosed tag before the affected region (parsing.md) |
| Page ignores the viewport / renders tiny on mobile | Missing <meta name="viewport"> |
width=device-width, initial-scale=1 (head.md) |
Accented characters render as é |
Charset declared late, or the HTTP header disagrees with the meta | Meta charset in the first 1024 bytes, UTF-8 everywhere (internationalization.md) |
| Layout behaves like it is 1998; box model wrong | Quirks mode from a missing or malformed doctype | <!DOCTYPE html> as the literal first bytes (parsing.md) |
| Link preview blank on Slack/X/iMessage | og:image relative, behind auth, or the page blocks the crawler |
Absolute URL, public, ≥1200×630 (head.md) |
Query string breaks after & (?a=1©=2 shows ©) |
Named character reference decoded in text content | In attributes it is safe; in text write & (parsing.md) |
<dialog> does not block the background |
Opened with show() instead of showModal() |
Only showModal() creates the top layer and the inert backdrop (interactive.md) |
| Focus tabs into a visually hidden panel | Hidden with opacity/transform, or aria-hidden only |
hidden, display:none, or inert — the four hiding mechanisms differ (accessibility.md) |
| Iframe blank, or console mentions frame-ancestors | Site refuses framing, or sandbox withholds a capability |
Check X-Frame-Options/CSP first, then the sandbox token list (media.md) |
| Autofill fills the wrong field | No explicit autocomplete; the browser guessed from the name |
Use the spec token (address-line1, cc-exp, one-time-code) (forms.md) |
| Email fine in Gmail, collapsed in Outlook desktop | Word rendering engine: no flex, no grid, no position |
Nested tables and inline styles (email.md) |
| Email cut off with "[Message clipped]" | Gmail clips a message past ~102 KB | Trim comments and duplicated inline CSS (email.md) |
| Anything else | Reduce to the smallest document that still shows it, then re-add one attribute at a time; the attribute that brings it back names the subsystem | auditing.md |
Implicit Defaults
Every one of these is a default that has produced a bug report against the application code.
| Thing | Default | Why it bites |
|---|---|---|
<button> inside a form |
type="submit" |
A "Cancel" or "Show password" button submits the form; write type="button" |
<form> method |
GET |
Credentials and PII land in the URL, the history, and the referrer |
<form> enctype with a file input |
application/x-www-form-urlencoded |
Only the file name is sent; needs multipart/form-data |
<input> type |
text |
A typo'd type silently degrades to a text box — no keyboard, no validation |
<script src> with no attribute |
Parser-blocking at that line | Rule 7 |
<img loading> |
eager |
Correct for the LCP image; wrong for everything below the fold |
<img> with no dimensions |
Reserves nothing | Rule 3 |
<table> source without <tbody> |
Parser inserts one | table > tr selectors and DOM walks never match |
<textarea> content |
Literal, whitespace preserved | Indenting the source injects leading spaces and newlines into the value |
autocomplete unspecified |
Browser heuristic | Right guess most of the time; wrong on address, card, and OTP fields |
hidden attribute |
display: none, but CSS wins |
hidden plus any display rule renders the element visible |
aria-hidden="true" |
Removed from the accessibility tree only | Still focusable and still in the tab order — a focus trap for keyboard users |
<a target="_blank"> |
Implicit noopener in current browsers |
Older webviews and embedded browsers still leak window.opener; keep rel="noopener" |
<iframe> with no sandbox |
Full capabilities of its own origin | Scripts, forms, popups, top-level navigation all allowed |
<input type="number"> |
Spinner, locale-dependent parsing, silent value drop on non-numeric | Use inputmode="numeric" + pattern for codes, phone numbers, and card fields |
| Missing doctype | Quirks mode | Legacy box model and dozens of divergences (parsing.md) |
Element Defaults
One default per need, with the escape hatch and its price. Full patterns and keyboard contracts: interactive.md.
| Need | Default | Switch when |
|---|---|---|
| Modal | <dialog> + showModal() |
Never for a true modal — it is the only element that gets the top layer and the inert backdrop for free |
| Transient popup, menu, tooltip | Popover attribute API | You need a focus trap (→ dialog) or must support pre-2024 browsers (→ ARIA pattern) |
| Accordion, disclosure, "read more" | <details> + <summary> |
You need the panel to remain in the DOM while animating height precisely, or a single-open group with roving focus |
| Tabs | No native element — ARIA tabs pattern | — |
| Autocomplete over a short fixed list | <input list> + <datalist> |
You need custom rendering, async results, or multi-select (→ combobox pattern, ~200 lines of JS) |
| Icon | Inline <svg aria-hidden="true"> beside text |
The icon is the control — then it needs an accessible name (Rule 4) |
| Progress and gauges | <progress>, <meter> |
— |
| Date, time, color, range | Native input types | The locale or range rules cannot be expressed natively — accept the full a11y contract of a custom widget |
| On/off switch | <input type="checkbox"> + role="switch" |
Never the switch attribute alone — it is not portable yet |
| Status message | aria-live="polite" region present in the DOM at load |
The message interrupts a task (→ assertive); a region injected at message time announces nothing |
| Sortable data grid | <table> + aria-sort |
Editable cells or virtualized rows (→ grid pattern) |
| Anything else | The element whose name matches the meaning; <div> only when nothing does |
— |
Output Gates
Before emitting markup, a review, or a fix:
- Does every interactive element have an accessible name, and is every one reachable and operable by keyboard alone?
- Does every image carry the right
altfor its job, and every<img>/<video>/<iframe>awidthandheight(Rules 3, 5)? - Does every form control have a
nameand a real label, and is everyidin the fragment unique (Rules 2, 4)? - Is the heading order a tree with no skipped level, and does each landmark appear where a screen-reader user would look for it?
- Would this parse the way it reads — no block inside
<p>, nothing loose inside a table, every element inside a valid parent (Rule 9)? - Is any content in this markup untrusted, and if so is it escaped for its exact context or run through an allowlist sanitizer (Rule 8)?
- Does anything user-facing depend on JS that has not shipped yet — a control that is inert until hydration, a link that is a
div? - Did anything durable come out of this — a page or template mapped, a rendering quirk, an audit result, a canonical host, a pattern that finally worked? Then it is written to its box in
memory-template.md, with its## Boxesline, in this same turn.
Configuration
User-dependent variables. Defaults apply until the user states a preference; store them in ~/Clawic/data/html/config.yaml.
| Variable | Type | Default | Effect |
|---|---|---|---|
| markup_flavor | html | jsx | vue | svelte | jinja | erb | handlebars | html | Syntax of every emitted snippet: attribute casing, self-closing form, boolean attributes, and which escaping helper is named in security.md |
| browser_support | evergreen | widely-available | legacy | widely-available | Gates what ships bare: evergreen allows popover and declarative shadow DOM unqualified; widely-available pairs them with a fallback; legacy blocks them and adds the pre-2020 equivalents (interactive.md) |
| a11y_target | wcag-a | wcag-aa | wcag-aaa | wcag-aa | Which row of the conformance table gates output in accessibility.md and auditing.md: AA = 4.5:1 body text and 24×24 CSS px targets; AAA = 7:1 and 44×44 |
| document_lang | text (BCP-47) | en | The lang on every generated <html>, the assumed direction, and the default hreflang self-reference (internationalization.md) |
| structured_data | none | json-ld | microdata | json-ld | Format and placement of schema markup emitted by head.md |
| inline_code_policy | allowed | nonce | forbidden | allowed | Whether emitted markup may carry inline <script>, <style> and on* handlers, ships them with a nonce placeholder, or moves them to external files (security.md) |
| untrusted_html | escape | sanitize | trusted-types | escape | The default treatment of user-supplied markup and which pipeline security.md recommends |
| email_client_floor | modern | outlook-desktop | outlook-desktop | Whether email.md emits table layout, VML and inline styles, or allows a single-column modern build |
| output_shape | fragment | full-document | fragment | Whether an answer returns the changed elements in context or a complete document |
Preference areas — customizable dimensions; a stated preference gets recorded in config.yaml and applied from then on:
- Tooling — formatter and linter (Prettier, html-validate, axe in CI), CMS or template engine, component library, which validator is authoritative — affects the shape of every example and the gates in
auditing.md - Conventions — attribute order and quoting, self-closing style on void elements,
data-*naming, id and class conventions, comment density, one-file-per-component vs page templates — affects emitted markup - Platform — target surfaces (web app, marketing site, email, print, embedded webview), device mix, whether RTL or CJK is in scope, minimum screen and pointer type — affects
media.md,email.md,internationalization.md - Safety posture — how loudly to flag accessibility regressions, whether third-party embeds and inline analytics may be emitted at all, appetite for
dangerously-style raw HTML injection — affects Output Gates andsecurity.md - Integrations — analytics, consent management, icon and font providers, image CDN, the ESP an email will be sent through — affects
head.md,performance.md,email.md - Constraints — banned elements or embeds, a strict CSP regime, a no-JS requirement, legacy pages that must keep working, brand rules on titles and social images — affects what may be proposed at all
- Output register — diff vs whole file, how much of the rationale ships with the fix, whether alternatives are shown before the recommendation
- Cadence — accessibility re-audit, validation sweep, email client re-test, third-party embed review — every accepted cadence becomes a row in the
## Duetable ofmemory.md
Traps
| Trap | Why it fails | Do instead |
|---|---|---|
<div onclick> as a button |
No keyboard, no focus, no role, no disabled state, no form submission | <button type="button">; style it flat (Rule 1) |
aria-label on a <div> or <span> |
Names are only exposed on elements with a role that supports naming — on a bare div it is ignored |
Give it a role that takes a name, or use the right element |
| Fixing an ARIA warning by adding more ARIA | Redundant or conflicting roles override the native semantics that were already correct | Remove the role; role="button" on <button> is noise, role="text" erases children |
| Headings chosen by size | h4 used for a section under an h2 breaks the outline every screen-reader user navigates by |
Level by depth, size by CSS (semantics.md) |
tabindex="1" and above |
Jumps that element ahead of the entire natural order, everywhere on the page | Only 0 and -1 are ever correct (accessibility.md) |
<p> used as a layout wrapper |
Any block child closes it — you get two empty paragraphs and orphaned content | <div> for grouping, <p> for prose (Rule 9) |
Nested <a> or <button> inside <a> |
Illegal content model; the parser un-nests them and the click target becomes unpredictable | One interactive element per interactive element |
<br> for spacing |
Announced as line breaks and impossible to style responsively | Margin on block elements |
Trusting required and pattern |
Client validation is a UX affordance; anything can POST to the endpoint | Validate on the server; use the attributes for the keyboard and the error timing |
placeholder used as the label |
Vanishes on input, low contrast, and unreadable to anyone who tabs back | Visible <label> (Rule 4) |
| Lazy-loading the hero image | Defers the LCP element and defeats the preload scanner — measurably slower | loading="eager" and fetchpriority="high" on the LCP image (performance.md) |
One .ico as the whole favicon set |
Misses dark mode, high-DPI, and every mobile install surface | The three-file floor plus a manifest (head.md) |
Blocklist sanitizing (strip <script>) |
<img onerror>, javascript: URLs, SVG handlers, and mutation-XSS all survive it |
Allowlist sanitizer, escaped by context (Rule 8) |
sandbox="allow-scripts allow-same-origin" on same-origin content |
The frame can reach into its own parent document and remove the sandbox attribute | Serve framed content from a distinct origin (security.md) |
| Copying the desktop page into an email | Flex, grid, position and external CSS are absent from the Word engine |
Table layout, inline styles, 600px (email.md) |
| A markup decision that lives only in the chat | Re-litigated every quarter by whoever inherits the template | artifacts/ with the date and what was rejected (memory-template.md) |
Where Experts Disagree
- How much ARIA is healthy. One school treats every ARIA attribute as debt to be justified; the other builds design-system components on ARIA patterns because the native elements do not compose. The frontier is ownership: if a component has tests and a maintainer, an ARIA pattern is fine; if it is one page written once, the native element wins because nothing will maintain the contract.
<section>vs<div>.<section>with no accessible name adds a region nobody can navigate to and clutters the landmark list;<section aria-labelledby>is genuinely useful. Default:<div>for grouping,<section>only when it has a heading you would want in the landmark list.- Web components in content sites. Encapsulation and portability against a hard cost: nothing renders until the definition loads, and forms, labels and CSS all need explicit bridges across the shadow boundary. Declarative shadow DOM narrows the gap but does not close it (
templates.md). - Semantic markup and SEO. Search engines do not rank a
<article>above a<div>, and claiming they do discredits the real argument: semantics buy assistive-tech behavior, browser features, and future parsers. Make the accessibility case; the metadata that genuinely affects search is inhead.md. - Email builders vs hand-written email. Builders survive client quirks that a hand-written template rediscovers; hand-written wins on size, dark mode control, and not being clipped at 102 KB. The frontier is whether anyone will re-test on client updates (
email.md).
Security & Privacy
Credentials: this skill reads and writes markup files. It does NOT store, log, copy, or transmit API keys, verification tokens, session values, or ESP credentials found in markup, and never writes one into ~/Clawic/data/.
Local storage: preferences, page and template notes, rendering quirks, audit results and generated artifacts stay in ~/Clawic/data/html/ on this machine, plus hostname rows in the shared ~/Clawic/data/domains/. URLs, element names and attribute names only — no secrets.
Guardrails: third-party embeds are named with the origin they contact and the capabilities they request before being proposed. Markup that renders user-supplied content is never emitted without its escaping or sanitizing step.
Related Skills
More Clawic skills, get them at https://clawic.com/skills/html (install if the user confirms):
css— layout, cascade, theming, and the visual half of everything herejavascript— DOM scripting, events, and the behavior an ARIA pattern promisesaccessibility— audits, assistive-technology testing, and WCAG beyond the markup layer (planned)seo— search strategy, indexing, and content that rankssvg— inline vector graphics,viewBox, and icon systems
Feedback
- If useful, star it: https://clawic.com/skills/html
- Latest version: https://clawic.com/skills/html
Part of Clawic, the verified skill library. Get this skill: https://clawic.com/skills/html.