HTML Reference · CLN v2.2 · Feb 2026
HTML: Attributes
= global, data-*, ARIA — every attribute with its purpose and gotchas ! boolean attributes: presence = true, any value including "false" = true # #html #attributes #aria #data #a11y
Attributes modify elements. Global attributes work on any element. ARIA attributes extend accessibility. data-* lets you store custom data. Hover operators · click spoilers to test.

01/Global AttributesOn any element
// ##global · Work on every HTML element
Identity & Styling
.id — unique identifier in the document
!must be unique — duplicate IDs break querySelector, CSS, and a11y
!no spaces · case-sensitive · can't start with a digit in CSS selectors
.class — space-separated list of CSS classes
*multiple classes: class="btn btn-primary active"
.style — inline CSS declaration
-highest specificity — overrides external CSS, hard to override Only use for truly dynamic values set via JS (e.g. position during drag). Never for static styling — that belongs in CSS classes.
Behaviour & Meta
.hidden — removes element from display and accessibility tree
~hidden vs display:none hidden is semantic — it means "not yet relevant". display:none is visual. CSS can override hidden (display:block beats it). Use visibility:hidden to hide visually while keeping in a11y tree. Use .sr-only class to keep in a11y tree but hide visually.
.tabindex — keyboard focus order
=0 — focusable in natural DOM order
=-1 — focusable via JS only (focus()), not by Tab
=1+ — explicit order · avoid — creates maintenance nightmare
.contenteditable="true" — makes element editable by user
.draggable="true" — enables drag API
.lang="en" — language of element content
*on <html> = document language · use on any element with different language
.dir="ltr"|"rtl"|"auto" — text direction
.title — tooltip on hover · also used for abbreviation expansion
!not accessible to keyboard/touch users — never use as sole source of critical info
.translate="no" — tells translation services not to translate (brand names, code)
.spellcheck="false" — disables spell checking (code editors, etc.)
?What is the difference between hidden and display:none? hidden is an HTML attribute with semantic meaning ("this content is not currently relevant"). display:none is a CSS declaration. Key difference: CSS can override hidden — a rule display:block on a [hidden] element makes it visible. hidden also removes from accessibility tree. Use hidden for progressive disclosure; use CSS for layout-driven visibility.

02/data-* Custom AttributesCustom Data
// ##data · Store custom data on elements
=data-* = any attribute starting with data- — custom data embedded in HTML
.naming: data- + any lowercase name, hyphens allowed
*data-user-id, data-price, data-theme, data-cln-type
.read in JS via element.dataset.userId (camelCase conversion)
=data-user-id → dataset.userId — hyphens become camelCase
.CSS can read via attr(data-label) in content property
+separates data from logic — no hidden inputs, no JS globals
+survives DOM mutations — data moves with the element
!do not store sensitive data — HTML source is public
!not intended for SEO — search engines may ignore data-* values
?You have data-product-sku="XY-001". How do you read it in JS? element.dataset.productSku — hyphens in the attribute name become camelCase in dataset. data-product-sku → dataset.productSku. You can also use getAttribute('data-product-sku') for the raw string.

03/ARIA AttributesAccessibility
// ##aria · Accessible Rich Internet Applications
=ARIA attributes add accessibility information that HTML alone can't express
!rule 1: use semantic HTML first — ARIA is a supplement, not a replacement
→<button> beats <div role="button" tabindex="0"> every time
!no ARIA is better than bad ARIA — wrong roles actively mislead screen readers
Roles
=role="value" — overrides or assigns element's ARIA role
.button · link · checkbox · radio — widget roles
.banner · main · navigation · contentinfo — landmark roles
.alert · status · log — live region roles
.dialog · alertdialog — modal roles
.presentation · none — removes semantic meaning
*use on decorative images: <img role="presentation" alt="">
State & Properties
.aria-label="text" — accessible name (no visible label)
*icon button: <button aria-label="Close">✕</button>
.aria-labelledby="id" — name comes from another element
.aria-describedby="id" — description from another element
*links error message to input field — see Forms §06
.aria-expanded="true|false" — toggle state (accordion, dropdown)
.aria-hidden="true" — remove from accessibility tree (decorative)
!never on focusable elements — keyboard users can't reach hidden controls
.aria-live="polite|assertive" — announce dynamic content changes
=polite — announces after current speech · assertive = interrupts
.aria-selected · aria-checked · aria-disabled — widget states
.aria-controls="id" — this element controls another
.aria-current="page|step|date" — current item in set
*nav item for current page: aria-current="page"
?When should you use aria-label vs aria-labelledby? aria-label: write the label text directly in the attribute — use when no visible label exists (icon buttons, search inputs). aria-labelledby: point to an existing element's ID — use when a visible heading or text already describes the element. labelledby is preferred when possible — it reuses existing visible text, keeping screen and visual experience in sync.
?A dropdown menu opens/closes. Which ARIA attribute tracks its state? aria-expanded="true|false" on the trigger button. When menu is open: aria-expanded="true". When closed: aria-expanded="false". Screen reader announces "button, collapsed" or "button, expanded". Also add aria-controls="menu-id" to link the button to the menu element.

04/Inline Event HandlersAvoid in production
// ##events · HTML event attributes — know them to avoid them
=inline event handlers: onclick, onsubmit, onchange, oninput…
-mixes behaviour and structure — hard to maintain, test, or CSP-harden
-only one handler per event — onclick overwrites previous value
→use addEventListener in JS — multiple handlers, easy removal, testable
+exception: prototyping, small demos, CLN wiki pages — acceptable for brevity
.common inline handlers:
=onclick · ondblclick — mouse clicks
=onsubmit · onreset — form events (on form element)
=oninput · onchange · onfocus · onblur — input events
=onkeydown · onkeyup — keyboard events
=onload · onerror — resource load events (img, script)

05/Boolean AttributesCommon Confusion
// ##boolean · Presence = true, any value including "false" = true
=boolean attribute: its presence means true · absence means false
!disabled="false" — element IS disabled · "false" string is ignored, presence counts
→to make it false: remove the attribute entirely — don't set it to "false"
.valid forms: disabled · disabled="" · disabled="disabled" — all equivalent
Form boolean attrs
.disabled — control inactive, not submitted
.readonly — not editable, but IS submitted
.required — must have value before submit
.checked — default checked state (checkbox/radio)
.selected — default selected option
.multiple — allows multiple values
.autofocus — focus on page load
.novalidate — skip form validation
Media & Other boolean attrs
.autoplay — start playing immediately
!blocked by browsers unless muted is also present
.muted — start muted
.controls — show native media controls
.loop — replay after end
.defer — script executes after DOM parsed
.async — script loads and executes asynchronously
.open — <details> expanded by default
.ismap — image is a server-side image map
?You write disabled="false" expecting the button to be enabled. What happens? The button is disabled. Boolean attributes work by presence/absence, not value. The string "false" is irrelevant. To enable: remove the attribute entirely. In JS: element.removeAttribute('disabled') — not element.setAttribute('disabled', 'false').
?Why does autoplay often not work even when present? Browsers block autoplay with sound to prevent intrusive audio on page load. Autoplay works when: (1) muted attribute is also present, (2) user has previously interacted with the site, or (3) browser has granted autoplay permission. Always combine autoplay with muted for reliable behaviour.