(** Typed error codes for HTML5 validation messages. This module defines a comprehensive hierarchy of validation errors using polymorphic variants, organized by error category. Each error type is documented with the specific HTML5 conformance requirement it represents. The error hierarchy is: - {!t} is the top-level type containing all errors wrapped by category - Each category (e.g., {!attr_error}, {!aria_error}) groups related errors - Inline descriptors like [[\`Attr of string]] provide self-documenting parameters {2 Example Usage} {[ (* Category-level matching *) let is_accessibility_error = function | `Aria _ | `Li_role _ -> true | _ -> false (* Fine-grained matching *) match err with | `Attr (`Duplicate_id (`Id id)) -> handle_duplicate id | `Img `Missing_alt -> suggest_alt_text () | _ -> default_handler err ]} *) (** {1 Severity} *) (** Severity level of a validation message. - [Error]: Conformance error that must be fixed - [Warning]: Likely problem that should be reviewed - [Info]: Suggestion for best practices *) type severity = Error | Warning | Info (** {1 Attribute Errors} Errors related to HTML attributes: disallowed attributes, missing required attributes, invalid attribute values, and duplicate IDs. *) (** Attribute-related validation errors. These errors occur when attributes violate HTML5 content model rules: - Attributes used on elements where they're not allowed - Required attributes that are missing - Attribute values that don't match expected formats - Duplicate ID attributes within a document *) type attr_error = [ | `Not_allowed of [`Attr of string] * [`Elem of string] (** Attribute is not in the set of allowed attributes for this element. Per HTML5 spec, each element has a defined set of content attributes; using attributes outside this set is a conformance error. Example: [type] attribute on a [
] element. *) | `Not_allowed_here of [`Attr of string] (** Attribute is valid on this element type but not in this context. Some attributes are only allowed under specific conditions, such as the [download] attribute which requires specific ancestor elements. *) | `Not_allowed_when of [`Attr of string] * [`Elem of string] * [`Condition of string] (** Attribute conflicts with another attribute or element state. Example: [readonly] and [disabled] together, or [multiple] on certain input types where it's not supported. *) | `Missing of [`Elem of string] * [`Attr of string] (** Element is missing a required attribute. Per HTML5, certain elements have required attributes for conformance. Example: [] requires [src] or [srcset], [] requires [type]. *) | `Missing_one_of of [`Elem of string] * [`Attrs of string list] (** Element must have at least one of the listed attributes. Some elements require at least one from a set of attributes. Example: [] needs [href] or [target] (or both). *) | `Bad_value of [`Elem of string] * [`Attr of string] * [`Value of string] * [`Reason of string] (** Attribute value doesn't match the expected format or enumeration. HTML5 defines specific value spaces for many attributes (enumerations, URLs, integers, etc.). This error indicates the value is malformed. *) | `Bad_value_generic of [`Message of string] (** Generic bad attribute value with custom message. Used when the specific validation failure requires a custom explanation that doesn't fit the standard bad value template. *) | `Duplicate_id of [`Id of string] (** Document contains multiple elements with the same ID. Per HTML5, the [id] attribute must be unique within a document. Duplicate IDs cause problems with fragment navigation, label association, and JavaScript DOM queries. *) | `Data_invalid_name of [`Reason of string] (** Custom data attribute name violates naming rules. [data-*] attribute names must be valid XML NCNames (no colons, must start with letter or underscore). The reason explains the specific naming violation. *) | `Data_uppercase (** Custom data attribute name contains uppercase letters. [data-*] attribute names must not contain ASCII uppercase letters (A-Z) per HTML5. Use lowercase with hyphens instead. *) ] (** {1 Element Structure Errors} Errors related to element usage, nesting, and content models. *) (** Element structure validation errors. These errors occur when elements violate HTML5 content model rules: - Obsolete elements that should be replaced - Elements used in wrong contexts (invalid parent/child relationships) - Missing required child elements - Empty elements that must have content *) type element_error = [ | `Obsolete of [`Elem of string] * [`Suggestion of string] (** Element is obsolete and should not be used. HTML5 obsoletes certain elements from HTML4 (e.g., [], [
]). The suggestion provides the recommended modern alternative. *) | `Obsolete_attr of [`Elem of string] * [`Attr of string] * [`Suggestion of string option] (** Attribute on this element is obsolete. Some attributes are obsolete on specific elements but may be valid elsewhere. Example: [align] on [] (use CSS instead). *) | `Obsolete_global_attr of [`Attr of string] * [`Suggestion of string] (** Global attribute is obsolete on all elements. Attributes like [bgcolor] are obsolete everywhere in HTML5. *) | `Not_allowed_as_child of [`Child of string] * [`Parent of string] (** Element cannot be a child of the specified parent. HTML5 defines content models for each element specifying which children are allowed. Example: [
] inside [

] is invalid. *) | `Unknown of [`Elem of string] (** Element name is not recognized. The element is not defined in HTML5, SVG, or MathML specs. May be a typo or a custom element without hyphen. *) | `Must_not_descend of [`Elem of string] * [`Attr of string option] * [`Ancestor of string] (** Element must not appear as descendant of the specified ancestor. Some elements have restrictions on their ancestry regardless of direct parent. Example: [
] cannot be nested inside []. The optional attribute indicates a conditional restriction. *) | `Missing_child of [`Parent of string] * [`Child of string] (** Parent element is missing a required child element. Some elements must contain specific children for conformance. Example: [

] requires [
] and [
] children. *) | `Missing_child_one_of of [`Parent of string] * [`Children of string list] (** Parent must contain at least one of the listed child elements. Example: [] must contain [] or []. *) | `Missing_child_generic of [`Parent of string] (** Parent is missing an unspecified required child. Used when the required child depends on context. *) | `Must_not_be_empty of [`Elem of string] (** Element must have content and cannot be empty. Some elements require text or child element content. Example: [] must not be empty. *) | `Text_not_allowed of [`Parent of string] (** Text content is not allowed in this element. Some elements only allow element children, not text. Example: [<table>] cannot contain direct text children. *) ] (** {1 Tag and Parse Errors} Errors from the parsing phase related to tags and document structure. *) (** Tag-level parse errors. These errors occur during HTML parsing when the parser encounters problematic tag structures or reaches end-of-file unexpectedly. *) type tag_error = [ | `Stray_start of [`Tag of string] (** Start tag appears in a position where it's not allowed. The parser encountered an opening tag that cannot appear in the current insertion mode. Example: [<tr>] outside [<table>]. *) | `Stray_end of [`Tag of string] (** End tag appears without a matching start tag. The parser encountered a closing tag with no corresponding open element in scope. *) | `End_for_void of [`Tag of string] (** End tag for a void element that cannot have one. Void elements ([<br>], [<img>], etc.) cannot have end tags in HTML5. Example: [</br>] is invalid. *) | `Self_closing_non_void (** Self-closing syntax [/>] used on non-void HTML element. In HTML5, [/>] is only meaningful on void elements and foreign (SVG/MathML) elements. On other elements it's ignored. *) | `Not_in_scope of [`Tag of string] (** End tag seen but no matching element in scope. The parser found a closing tag but the element isn't in the current scope (may be blocked by formatting elements). *) | `End_implied_open of [`Tag of string] (** End tag implied closing of other open elements. The parser had to implicitly close elements to process this end tag, indicating mismatched nesting. *) | `Start_in_table of [`Tag of string] (** Start tag appeared inside table where it's foster-parented. When certain tags appear in table context, they're moved outside the table (foster parenting), indicating malformed markup. *) | `Bad_start_in of [`Tag of string] * [`Context of string] (** Start tag appeared in invalid context. Generic error for tags in wrong parsing contexts. *) | `Eof_with_open (** End of file reached with unclosed elements. The document ended with elements still open on the stack, indicating missing closing tags. *) ] (** Character reference errors. These errors occur when character references (like [&] or [A]) expand to problematic Unicode code points. *) type char_ref_error = [ | `Forbidden_codepoint of [`Codepoint of int] (** Character reference expands to a forbidden code point. Certain code points are forbidden in HTML documents (e.g., NULL U+0000, noncharacters). These cannot appear even via character references. *) | `Control_char of [`Codepoint of int] (** Character reference expands to a control character. C0 and C1 control characters (except tab, newline, etc.) are problematic and trigger this warning. *) | `Non_char of [`Codepoint of int] * [`Astral of bool] (** Character reference expands to a Unicode noncharacter. Noncharacters (like U+FFFE, U+FFFF) are reserved and should not appear in documents. Astral flag indicates if it's in the supplementary planes. *) | `Unassigned (** Character reference expands to permanently unassigned code point. The referenced code point will never be assigned a character. *) | `Zero (** Character reference expands to U+0000 (NULL). NULL is replaced with U+FFFD (replacement character) per HTML5. *) | `Out_of_range (** Character reference value exceeds Unicode maximum. Numeric character references must be <= U+10FFFF. *) | `Carriage_return (** Numeric character reference expanded to carriage return. CR (U+000D) via numeric reference is replaced with LF. *) ] (** {1 ARIA and Accessibility Errors} Errors related to WAI-ARIA attributes and accessibility conformance. *) (** ARIA and role validation errors. These errors ensure proper usage of WAI-ARIA attributes and roles for accessibility. Incorrect ARIA can make content less accessible than having no ARIA at all. *) type aria_error = [ | `Unnecessary_role of [`Role of string] * [`Elem of string] * [`Reason of string] (** Role is redundant because element has implicit role. Many HTML elements have implicit ARIA roles; explicitly setting the same role is unnecessary. Example: [role="button"] on [<button>]. *) | `Bad_role of [`Elem of string] * [`Role of string] (** Role value is invalid or not allowed on this element. The role is either not a valid ARIA role token or is not permitted on this particular element type. *) | `Must_not_specify of [`Attr of string] * [`Elem of string] * [`Condition of string] (** ARIA attribute must not be specified in this situation. Some ARIA attributes are prohibited on certain elements unless specific conditions are met. *) | `Must_not_use of [`Attr of string] * [`Elem of string] * [`Condition of string] (** ARIA attribute must not be used with this element configuration. The attribute conflicts with another attribute or state of the element. *) | `Should_not_use of [`Attr of string] * [`Role of string] (** ARIA attribute should not be used with this role (warning). While not strictly invalid, the attribute is discouraged with this role as it may cause confusion. *) | `Hidden_on_body (** [aria-hidden="true"] used on body element. Hiding the entire document from assistive technology is almost certainly an error. *) | `Unrecognized_role of [`Token of string] (** Unrecognized role token was discarded. The role attribute contained a token that isn't a valid ARIA role. Browsers ignore unknown role tokens. *) | `Tab_without_tabpanel (** Tab element has no corresponding tabpanel. Each [role="tab"] should control a [role="tabpanel"]. Missing tabpanels indicate incomplete tab interface. *) | `Multiple_main (** Document has multiple visible main landmarks. Only one visible [role="main"] or [<main>] should exist per document for proper landmark navigation. *) | `Accessible_name_prohibited of [`Attr of string] * [`Elem of string] (** Accessible name attribute not allowed on element with generic role. Elements with implicit [role="generic"] (or no role) cannot have [aria-label], [aria-labelledby], or [aria-braillelabel] unless they have an explicit role that supports accessible names. *) ] (** List item role constraint errors. Special ARIA role restrictions on [<li>] elements and [<div>] children of [<dl>] elements. *) type li_role_error = [ | `Div_in_dl_bad_role (** [<div>] child of [<dl>] has invalid role. When [<div>] is used to group [<dt>]/[<dd>] pairs in a [<dl>], it may only have [role="presentation"] or [role="none"]. *) | `Li_bad_role_in_menu (** [<li>] in menu/menubar has invalid role. [<li>] descendants of [role="menu"] or [role="menubar"] must have roles like [menuitem], [menuitemcheckbox], etc. *) | `Li_bad_role_in_tablist (** [<li>] in tablist has invalid role. [<li>] descendants of [role="tablist"] must have [role="tab"]. *) | `Li_bad_role_in_list (** [<li>] in list context has invalid role. [<li>] in [<ul>], [<ol>], [<menu>], or [role="list"] must have [role="listitem"] or no explicit role. *) ] (** {1 Table Errors} Errors in HTML table structure and cell spanning. *) (** Table structure validation errors. These errors indicate problems with table structure that may cause incorrect rendering or accessibility issues. *) type table_error = [ | `Row_no_cells of [`Row of int] (** Table row has no cells starting on it. The specified row number (1-indexed) in an implicit row group has no cells beginning on that row, possibly due to rowspan. *) | `Cell_overlap (** Table cells overlap due to spanning. A cell's rowspan/colspan causes it to overlap with another cell, making the table structure ambiguous. *) | `Cell_spans_rowgroup (** Cell's rowspan extends past its row group. A cell's rowspan would extend beyond the [<tbody>], [<thead>], or [<tfoot>] containing it; the span is clipped. *) | `Column_no_cells of [`Column of int] * [`Elem of string] (** Table column has no cells. A column established by [<col>] or [<colgroup>] has no cells beginning in it, indicating mismatched column definitions. *) ] (** {1 Internationalization Errors} Errors related to language declaration and text direction. *) (** Language and internationalization validation errors. These errors help ensure documents properly declare their language and text direction for accessibility and correct rendering. *) type i18n_error = [ | `Missing_lang (** Document has no language declaration. The [<html>] element should have a [lang] attribute declaring the document's primary language for accessibility. *) | `Wrong_lang of [`Detected of string] * [`Declared of string] * [`Suggested of string] (** Declared language doesn't match detected content language. Automatic language detection suggests the [lang] attribute value is incorrect for the actual content. *) | `Missing_dir_rtl of [`Language of string] (** RTL language content lacks [dir="rtl"]. Content detected as a right-to-left language should have explicit direction declaration. *) | `Wrong_dir of [`Language of string] * [`Declared of string] (** Text direction doesn't match detected language direction. The [dir] attribute value conflicts with the detected language's natural direction. *) | `Xml_lang_without_lang (** [xml:lang] present but [lang] is missing. When [xml:lang] is specified (for XHTML compatibility), the [lang] attribute must also be present with the same value. *) | `Xml_lang_mismatch (** [xml:lang] and [lang] attribute values don't match. Both attributes must have identical values when present. *) | `Not_nfc of [`Replacement of string] (** Text is not in Unicode Normalization Form C. HTML5 requires NFC normalization. The replacement string shows the correctly normalized form. *) ] (** {1 Import Map Errors} Errors in [<script type="importmap">] JSON content. *) (** Import map validation errors. These errors occur when validating the JSON content of [<script type="importmap">] elements per the Import Maps spec. *) type importmap_error = [ | `Invalid_json (** Import map content is not valid JSON. The script content must be parseable as JSON. *) | `Invalid_root (** Import map root is not a valid object. The JSON must be an object with only [imports], [scopes], and [integrity] properties. *) | `Imports_not_object (** The [imports] property is not a JSON object. [imports] must be an object mapping specifiers to URLs. *) | `Empty_key (** Specifier map contains an empty string key. Module specifier keys must be non-empty strings. *) | `Non_string_value (** Specifier map contains a non-string value. All values in the specifier map must be strings (URLs). *) | `Key_trailing_slash (** Specifier with trailing [/] maps to URL without trailing [/]. When a specifier key ends with [/], its value must also end with [/] for proper prefix matching. *) | `Scopes_not_object (** The [scopes] property is not a JSON object. [scopes] must be an object with URL keys. *) | `Scopes_values_not_object (** A [scopes] entry value is not a JSON object. Each scope must map to a specifier map object. *) | `Scopes_invalid_url (** A [scopes] key is not a valid URL. Scope keys must be valid URL strings. *) | `Scopes_value_invalid_url (** A specifier value in [scopes] is not a valid URL. URL values in scope specifier maps must be valid. *) ] (** {1 Element-Specific Errors} Validation errors specific to particular HTML elements. *) (** Image element ([<img>]) validation errors. *) type img_error = [ | `Missing_alt (** Image lacks [alt] attribute for accessibility. Per WCAG and HTML5, images must have [alt] text describing their content, or [alt=""] for decorative images. *) | `Missing_src_or_srcset (** Image has neither [src] nor [srcset]. An [<img>] must have at least one image source specified. *) | `Empty_alt_with_role (** Image with [alt=""] has a [role] attribute. Decorative images (empty [alt]) must not have [role] because they should be hidden from assistive technology. *) | `Ismap_needs_href (** Image with [ismap] lacks [<a href>] ancestor. Server-side image maps require a link wrapper to function. *) ] (** Link element ([<link>]) validation errors. *) type link_error = [ | `Missing_href (** [<link>] has no [href] or [imagesrcset]. A link element must have a resource to link to. *) | `As_requires_preload (** [<link as="...">] used without [rel="preload"]. The [as] attribute is only meaningful for preload/modulepreload. *) | `Imagesrcset_requires_as_image (** [<link imagesrcset>] used without [as="image"]. Image srcset preloading requires [as="image"]. *) ] (** Label element ([<label>]) validation errors. *) type label_error = [ | `Too_many_labelable (** Label contains multiple labelable descendants. A [<label>] should associate with exactly one form control. *) | `For_id_mismatch (** Label's [for] doesn't match descendant input's [id]. When a [<label>] has both [for] and a descendant input, the input's [id] must match the [for] value. *) | `Role_on_ancestor (** [<label>] with role is ancestor of labelable element. Adding [role] to a label that wraps a form control breaks the implicit label association. *) | `Aria_label_on_ancestor (** [<label>] with [aria-label] is ancestor of labelable element. [aria-label] on a label that wraps a form control creates conflicting accessible names. *) | `Role_on_for (** [<label>] with role uses [for] association. Labels with explicit [for] association must not have [role]. *) | `Aria_label_on_for (** [<label>] with [aria-label] uses [for] association. [aria-label] on a label associated via [for] creates conflicting accessible names. *) ] (** Input element ([<input>]) validation errors. *) type input_error = [ | `Checkbox_needs_aria_pressed (** Checkbox with [role="button"] lacks [aria-pressed]. When a checkbox is styled as a toggle button, it needs [aria-pressed] to convey the toggle state. *) | `Value_constraint of [`Constraint of string] (** Input [value] doesn't meet type-specific constraints. Different input types have different value format requirements (dates, numbers, emails, etc.). *) | `List_not_allowed (** [list] attribute used on incompatible input type. The [list] attribute for datalist binding is only valid on certain input types (text, search, url, etc.). *) | `List_requires_datalist (** [list] attribute doesn't reference a [<datalist>]. The [list] attribute must contain the ID of a datalist element. *) ] (** Responsive image ([srcset]/[sizes]) validation errors. *) type srcset_error = [ | `Sizes_without_srcset (** [sizes] used without [srcset]. The [sizes] attribute is meaningless without [srcset]. *) | `Imagesizes_without_imagesrcset (** [imagesizes] used without [imagesrcset]. On [<link>], [imagesizes] requires [imagesrcset]. *) | `W_without_sizes (** [srcset] with width descriptors lacks [sizes]. When using width descriptors ([w]) in [srcset], the [sizes] attribute must specify the rendered size. *) | `Source_missing_srcset (** [<source>] in [<picture>] lacks [srcset]. Picture source elements must have a srcset. *) | `Source_needs_media_or_type (** [<source>] needs [media] or [type] to differentiate. When multiple sources exist, each must have selection criteria. *) | `Picture_missing_img (** [<picture>] lacks required [<img>] child. A picture element must contain an img as the fallback. *) ] (** SVG element validation errors. *) type svg_error = [ | `Deprecated_attr of [`Attr of string] * [`Elem of string] (** SVG attribute is deprecated. Certain SVG presentation attributes are deprecated in favor of CSS properties. *) | `Missing_attr of [`Elem of string] * [`Attr of string] (** SVG element missing required attribute. Some SVG elements have required attributes for valid rendering. *) ] (** Miscellaneous element-specific errors. These errors are specific to individual elements that don't warrant their own category. *) type misc_error = [ | `Option_empty_without_label (** [<option>] without [label] attribute is empty. Options need either text content or a label attribute. *) | `Bdo_missing_dir (** [<bdo>] element lacks required [dir] attribute. The bidirectional override element must specify direction. *) | `Bdo_dir_auto (** [<bdo>] has [dir="auto"] which is invalid. BDO requires explicit [ltr] or [rtl], not auto-detection. *) | `Base_missing_href_or_target (** [<base>] has neither [href] nor [target]. A base element must specify at least one of these. *) | `Base_after_link_script (** [<base>] appears after [<link>] or [<script>]. The base URL must be established before other URL resolution. *) | `Map_id_name_mismatch (** [<map>] [id] and [name] attributes don't match. For image maps, both attributes must have the same value. *) | `Summary_missing_role (** Non-default [<summary>] lacks [role] attribute. Custom summary content outside details needs explicit role. *) | `Summary_missing_attrs (** Non-default [<summary>] missing required ARIA attributes. Custom summary implementations need proper ARIA. *) | `Summary_role_not_allowed (** [<summary>] for its parent [<details>] has [role]. Default summary for details must not override its role. *) | `Autocomplete_webauthn_on_select (** [<select>] has [autocomplete] containing [webauthn]. WebAuthn autocomplete tokens are not valid for select elements. *) | `Commandfor_invalid_target (** [commandfor] doesn't reference a valid element ID. The invoker must reference an element in the same tree. *) | `Style_type_invalid (** [<style type>] has value other than [text/css]. HTML5 only supports CSS in style elements. *) | `Headingoffset_invalid (** [headingoffset] value is out of range. Must be an integer between 0 and 8. *) | `Media_empty (** [media] attribute is empty string. Media queries must be non-empty if the attribute is present. *) | `Media_all (** [media] attribute is just ["all"]. Using [media="all"] is pointless; omit the attribute instead. *) | `Multiple_h1 (** Document contains multiple [<h1>] elements. Best practice is one [<h1>] per document unless using [headingoffset] to indicate sectioning. *) | `Multiple_autofocus (** Multiple elements have [autofocus] in same scope. Only one element should have autofocus per scoping root. *) ] (** {1 Top-Level Error Type} *) (** All HTML5 validation errors, organized by category. Pattern match on the outer constructor to handle error categories, or match through to specific errors as needed. {[ let severity_of_category = function | `Aria _ -> may_be_warning | `I18n _ -> usually_info_or_warning | _ -> usually_error ]} *) type t = [ | `Attr of attr_error (** Attribute validation errors *) | `Element of element_error (** Element structure errors *) | `Tag of tag_error (** Tag-level parse errors *) | `Char_ref of char_ref_error (** Character reference errors *) | `Aria of aria_error (** ARIA and accessibility errors *) | `Li_role of li_role_error (** List item role constraints *) | `Table of table_error (** Table structure errors *) | `I18n of i18n_error (** Language and direction errors *) | `Importmap of importmap_error (** Import map JSON errors *) | `Img of img_error (** Image element errors *) | `Link of link_error (** Link element errors *) | `Label of label_error (** Label element errors *) | `Input of input_error (** Input element errors *) | `Srcset of srcset_error (** Responsive image errors *) | `Svg of svg_error (** SVG-specific errors *) | `Misc of misc_error (** Miscellaneous element errors *) | `Generic of string (** Fallback for messages without specific error codes *) ] (** {1 Functions} *) (** Get the severity level for an error. Most errors are [Error]; some ARIA and i18n issues are [Warning] or [Info]. *) val severity : t -> severity (** Get a short categorization code string. Useful for filtering, grouping, or machine-readable output. Example: ["disallowed-attribute"], ["missing-alt"], ["aria-not-allowed"]. *) val code_string : t -> string (** Convert error to human-readable message. Produces messages matching the Nu HTML Validator format with proper Unicode curly quotes around identifiers. *) val to_message : t -> string (** Format a string with Unicode curly quotes. Wraps the string in U+201C and U+201D ("..."). *) val q : string -> string (** {1 Error Construction Helpers} These functions simplify creating common error types. *) (** Create a bad attribute value error. Example: [bad_value ~element:"img" ~attr:"src" ~value:"" ~reason:"URL cannot be empty"] *) val bad_value : element:string -> attr:string -> value:string -> reason:string -> t (** Create a bad attribute value error with just a message. Example: [bad_value_msg "The value must be a valid URL"] *) val bad_value_msg : string -> t (** Create a missing required attribute error. Example: [missing_attr ~element:"img" ~attr:"alt"] *) val missing_attr : element:string -> attr:string -> t (** Create an attribute not allowed error. Example: [attr_not_allowed ~element:"span" ~attr:"href"] *) val attr_not_allowed : element:string -> attr:string -> t (** Create an element not allowed as child error. Example: [not_allowed_as_child ~child:"div" ~parent:"p"] *) val not_allowed_as_child : child:string -> parent:string -> t (** Create a must not be empty error. Example: [must_not_be_empty ~element:"title"] *) val must_not_be_empty : element:string -> t