Skip to content
Commits on Source (47)
......@@ -189,24 +189,34 @@ Jest compat:
npm rm rollup-plugin-dts
- >
npm install
@jest/core@${JEST_VERSION}
jest@${JEST_VERSION}
jest-circus@${JEST_VERSION}
jest-diff@${JEST_VERSION}
jest-environment-jsdom@${JEST_VERSION}
jest-runtime@${JEST_VERSION}
jest-snapshot@${JEST_VERSION}
ts-jest@${TS_JEST_VERSION}
typescript@${TS_VERSION}
@types/jest@${TYPES_VERSION}
- if [[ "${MATRIX}" -ge "28" ]]; then npm install @jest/expect@${JEST_VERSION}; fi
- npm dedupe
- >
npm ls
@jest/core
@jest/expect
jest
jest-circus
jest-diff
jest-environment-jsdom
jest-runtime
jest-snapshot
pretty-format
ts-jest
typescript
@types/jest
script:
- sed 's#/src/index.ts#/dist/cjs/index.js#g' -i package.json
- for spec in tests/jest/*.spec.ts; do sed 's#../../src#../../dist/cjs#g' -i "${spec}"; done
- npm exec jest -- --no-coverage --ci -- tests/jest
......
# html-validate changelog
## [7.7.0](https://gitlab.com/html-validate/html-validate/compare/v7.6.0...v7.7.0) (2022-10-23)
### Features
- **html5:** validates `<a target>` and `<area target>` for valid keywords ([6fa0bd9](https://gitlab.com/html-validate/html-validate/commit/6fa0bd9b823b746eeeab1d644c8748f288ea2730))
- new `defineMetadata` helper for writing custom element metadata ([6a06811](https://gitlab.com/html-validate/html-validate/commit/6a06811bb9d527648bc738ee464cfb5834fb038f)), closes [#186](https://gitlab.com/html-validate/html-validate/issues/186)
- **rules:** new rule `area-alt` ([3c1f0b3](https://gitlab.com/html-validate/html-validate/commit/3c1f0b3a6c2b24532d8c90894e54366bc79870e3)), closes [#178](https://gitlab.com/html-validate/html-validate/issues/178)
- **rules:** new rule `attribute-misuse` ([07a0bbe](https://gitlab.com/html-validate/html-validate/commit/07a0bbe15149d588c4c03956fdb410bc1dab236a)), closes [#181](https://gitlab.com/html-validate/html-validate/issues/181)
### Bug Fixes
- **html5:** `<map>` requires `name` attribute ([6104eb3](https://gitlab.com/html-validate/html-validate/commit/6104eb3e4874bcd1c97574994728dc1b2d489a42))
- **html5:** `<meta charset>` should only allow `utf-8` ([aaa15fe](https://gitlab.com/html-validate/html-validate/commit/aaa15fefdde59e0b8285c39ffa349f35d96cdb03))
- **html5:** disallow `<area coords>` when `shape` is `default` ([76115f2](https://gitlab.com/html-validate/html-validate/commit/76115f2388d61239f7f236877f38010daeb8444d)), closes [#183](https://gitlab.com/html-validate/html-validate/issues/183)
- **html5:** mark `<keygen>` as deprecated ([859402d](https://gitlab.com/html-validate/html-validate/commit/859402d11543052180829f1b0b8b7b2914dc3917))
- **html5:** require `<area coords>` when `shape` is requires is ([ed750c1](https://gitlab.com/html-validate/html-validate/commit/ed750c1a8f8b38469cc474a8ebb046470cd2f15a)), closes [#182](https://gitlab.com/html-validate/html-validate/issues/182)
## [7.6.0](https://gitlab.com/html-validate/html-validate/compare/v7.5.0...v7.6.0) (2022-10-10)
### Features
......
---
docType: content
title: API - MetadataHelper
id: api:MetadataHelper
name: MetadataHelper
---
# `MetadataHelper`
```ts
export interface MetadataHelper {
allowedIfAttributeIsPresent(...attr: string[]): MetaAttributeAllowedCallback;
allowedIfAttributeIsAbsent(...attr: string[]): MetaAttributeAllowedCallback;
allowedIfAttributeHasValue(
attr: string,
value: string[],
options?: { defaultValue?: string | null }
): MetaAttributeAllowedCallback;
}
```
These functions are exported as `metadataHelper` and can be used when writing element metadata:
```js
const { metadataHelper } = require("html-validate");
const { allowedIfAttributeIsPresent } = metadataHelper;
```
## `allowedIfAttributeIsPresent`
Returns an error if another attribute is omitted, i.e. it requires another attribute to be present to pass.
```js
const { defineMetadata, metadataHelper } = require("html-validate");
const { allowedIfAttributeIsPresent } = metadataHelper;
module.exports = defineMetadata({
"custom-element": {
attributes: {
foo: {
/* will be allowed if bar or baz is also present */
allowed: allowedIfAttributeIsPresent("bar", "baz"),
},
},
},
});
```
## `allowedIfAttributeIsAbsent`
Returns an error if another attribute is present, i.e. it requires another attribute to be omitted to pass.
```js
const { defineMetadata, metadataHelper } = require("html-validate");
const { allowedIfAttributeIsPresent } = metadataHelper;
module.exports = defineMetadata({
"custom-element": {
attributes: {
foo: {
/* will be allowed only if both bar or baz is omitted */
allowed: allowedIfAttributeIsAbsent("bar", "baz"),
},
},
},
});
```
## `allowedIfAttributeHasValue`
Returns an error if another attribute does not have one of the listed values.
```js
const { defineMetadata, metadataHelper } = require("html-validate");
const { allowedIfAttributeIsPresent } = metadataHelper;
module.exports = defineMetadata({
"custom-element": {
attributes: {
foo: {
/* will be allowed only if "type" attribute is set to "foo" or "bar", with the default being "foo" */
allowed: allowedIfAttributeHasValue("type", ["foo", "bar"], { defaultValue: "foo" }),
},
},
},
});
```
......@@ -12,12 +12,14 @@ A better method is to use inheritance.
Lets assume our `<my-component>` is actually a wrapper for an input field with a label and the content is what is used as `<label>`.
Thus by inheriting from `<label>` we automatically get the same rules.
```json
{
```js
const { defineMetadata } = require("html-validate");
module.exports = defineMetadata({
"my-component": {
"inherit": "label"
}
}
inherit: "label",
},
});
```
<validate name="inheritance" elements="inheritance.json">
......
......@@ -12,17 +12,19 @@ To define what values attribute accept the `attributes` property is used, to def
Assuming our `<my-component>` element has a `duck` attribute which can take the value `huey`, `dewey` or `louie` we can use the `attributes` property to define an enumerated list of allowed values:
```json
{
```js
const { defineMetadata } = require("html-validate");
module.exports = defineMetadata({
"my-component": {
"flow": true,
"attributes": {
"duck": {
"enum": ["huey", "dewey", "louie"]
}
}
}
}
flow: true,
attributes: {
duck: {
enum: ["huey", "dewey", "louie"],
},
},
},
});
```
<validate name="enum" elements="restrict-attributes-enum.json">
......@@ -32,17 +34,20 @@ Assuming our `<my-component>` element has a `duck` attribute which can take the
We can also specify regular expressions by surrounding the string with `/` (remember to escape special characters properly):
```json
{
"my-component": {
"flow": true,
"attributes": {
"ducks": {
"enum": ["/\\d+/"]
}
}
}
}
```diff
const { defineMetadata } = require("html-validate");
module.exports = defineMetadata({
"my-component": {
flow: true,
attributes: {
duck: {
- enum: ["huey", "dewey", "louie"],
+ enum: ["/\\d+/"],
},
},
},
});
```
<validate name="regexp" elements="restrict-attributes-regexp.json">
......@@ -60,17 +65,19 @@ We can also specify regular expressions by surrounding the string with `/` (reme
To force a boolean value similar to `disabled`, `selected` etc instead set the `boolean` property to `true`.
```json
{
```js
const { defineMetadata } = require("html-validate");
module.exports = defineMetadata({
"my-component": {
"flow": true,
"attributes": {
"quacks": {
"boolean": true
}
}
}
}
flow: true,
attributes: {
quacks: {
boolean: true,
},
},
},
});
```
<validate name="boolean" elements="restrict-attributes-boolean.json">
......@@ -83,18 +90,20 @@ This is often combined with `enum` but it should have a default value.
For instance, to allow the `quacks` attribute to be set to either `duck` or `dog` but at the same time not require a value to be set at all `omit` can be used.
```json
{
```js
const { defineMetadata } = require("html-validate");
module.exports = defineMetadata({
"my-component": {
"flow": true,
"attributes": {
"quacks": {
"omit": true,
"enum": ["duck", "dog"]
}
}
}
}
flow: true,
attributes: {
quacks: {
omit: true,
enum: ["duck", "dog"],
},
},
},
});
```
<validate name="omit" elements="restrict-attributes-omit.json">
......@@ -106,17 +115,19 @@ For instance, to allow the `quacks` attribute to be set to either `duck` or `dog
Required attributes (attributes that must be set on an element) can be specified by setting `required` to `true`:
```json
{
```js
const { defineMetadata } = require("html-validate");
module.exports = defineMetadata({
"my-component": {
"flow": true,
"attributes": {
"duck": {
"required": true
}
}
}
}
flow: true,
attributes: {
duck: {
required: true,
},
},
},
});
```
<validate name="required" elements="restrict-attributes-required.json">
......@@ -128,17 +139,19 @@ Required attributes (attributes that must be set on an element) can be specified
Similar to required attribute we can set `deprecated` to true or a message to mark an attribute as deprecated:
```json
{
```js
const { defineMetadata } = require("html-validate");
module.exports = defineMetadata({
"my-component": {
"flow": true,
"attributes": {
"duck": {
"deprecated": true
}
}
}
}
flow: true,
attributes: {
duck: {
deprecated: true,
},
},
},
});
```
<validate name="deprecated" elements="restrict-attributes-deprecated.json">
......
......@@ -9,13 +9,15 @@ Looking back at our initial example we saw that the element accepted a `<button>
If we want to allow only phrasing content (`<span>`, `<strong>`, etc) inside we can use the `permittedContent` property to limit.
`permittedContent` is a list of allowed tags or content categories.
```json
{
```js
const { defineMetadata } = require("html-validate");
module.exports = defineMetadata({
"my-component": {
"flow": true,
"permittedContent": ["span", "strong", "em"]
}
}
flow: true,
permittedContent: ["span", "strong", "em"],
},
});
```
<validate name="tags" elements="restrict-content-tags.json">
......@@ -26,24 +28,30 @@ If we want to allow only phrasing content (`<span>`, `<strong>`, etc) inside we
As it quickly get tedious to list all tag names we can refer to content categories directly:
```json
{
"my-component": {
"flow": true,
"permittedContent": ["@phrasing"]
}
}
```diff
const { defineMetadata } = require("html-validate");
module.exports = defineMetadata({
"my-component": {
flow: true,
- permittedContent: ["span", "strong", "em"],
+ permittedContent: ["@phrasing"],
},
});
```
The list can also be turned to a blacklist by using the `exclude` keyword:
```json
{
"my-component": {
"flow": true,
"permittedContent": [{ "exclude": "@heading" }]
}
}
```diff
const { defineMetadata } = require("html-validate");
module.exports = defineMetadata({
"my-component": {
flow: true,
- permittedContent: ["span", "strong", "em"],
+ permittedContent: [{ "exclude": "@heading" }],
},
});
```
<validate name="exclude" elements="restrict-content-exclude.json">
......@@ -68,13 +76,15 @@ Most of the time you should prefer `permittedContent` over `permittedDescendants
However, it can be used in circumstances where this is not possible.
The most common case is to prevent nesting of the component or limit usage of certain content categories such as sectioning or headings:
```json
{
```js
const { defineMetadata } = require("html-validate");
module.exports = defineMetadata({
"my-component": {
"flow": true,
"permittedDescendants": [{ "exclude": ["my-component", "@sectioning"] }]
}
}
flow: true,
permittedDescendants: [{ exclude: ["my-component", "@sectioning"] }],
},
});
```
<validate name="descendants" elements="restrict-content-descendants.json">
......@@ -109,14 +119,16 @@ Other properties to limit content also exits, check the [element metadata refere
(simplified for brevity)
```json
{
"html": {
"permittedContent": ["head?", "body?"],
"permittedOrder": ["head", "body"],
"requiredContent": ["head", "body"]
}
}
```js
const { defineMetadata } = require("html-validate");
module.exports = defineMetadata({
html: {
permittedContent: ["head?", "body?"],
permittedOrder: ["head", "body"],
requiredContent: ["head", "body"],
},
});
```
Like we seen before the `permittedContent` property is used to restrict to only accept the `<head>` and `<body>` elements.
......
......@@ -65,14 +65,16 @@ Most of the time it would make little sense to nest components but sometimes it
Perhaps the nesting isn't direct but happens way down in the DOM tree.
All of above considered valid unless the metadata gives instructions how the element should be used.
The first step is creating a new JSON-file, e.g. `elements.json` and configure the validator to read it.
The first step is creating a new file, e.g. `elements.js` and configure the validator to read it.
`elements.json`:
`elements.js`:
```json
{
"my-component": {}
}
```js
const { defineMetadata } = require("html-validate");
module.exports = defineMetadata({
"my-component": {},
});
```
`.htmlvalidate.json`:
......@@ -80,7 +82,7 @@ The first step is creating a new JSON-file, e.g. `elements.json` and configure t
```json
{
"extends": ["html-validate:recommended"],
"elements": ["html5", "./elements.json"]
"elements": ["html5", "./elements.js"]
}
```
......@@ -111,12 +113,14 @@ A bit simplified but flow elements can be thought as block-level `<div>` and phr
For instance, if our `<my-component>` element were to work similar to a `<div>` we can set the `flow` property to `true`.
```json
{
"my-component": {
"flow": true
}
}
```diff
const { defineMetadata } = require("html-validate");
module.exports = defineMetadata({
"my-component": {
+ flow: true,
},
});
```
The element will now be accepted inside another `<div>` as flow elements can be nested inside each other.
......@@ -137,13 +141,15 @@ It can not be nested inside a `<span>` as a `<span>` does not accept flow conten
If we set the `phrasing` property as well the element will be allowed inside a `<span>` too:
```json
{
"my-component": {
"flow": true,
"phrasing": true
}
}
```diff
const { defineMetadata } = require("html-validate");
module.exports = defineMetadata({
"my-component": {
flow: true,
+ phrasing: true,
},
});
```
<validate name="phrasing-metadata" elements="simple-component-phrasing.json" results="true">
......@@ -169,13 +175,15 @@ There are other content categories as well, check the [element metadata referenc
## Case study: `<div>`
```json
{
"div": {
"flow": true,
"permittedContent": ["@flow"]
}
}
```js
const { defineMetadata } = require("html-validate");
module.exports = defineMetadata({
div: {
flow: true,
permittedContent: ["@flow"],
},
});
```
As a generic flow content container the `<div>` element simply sets the `flow` property and uses the `permittedContent` property to restrict its content to only allow other flow content (in practice this means almost all other elements).
......
......@@ -3,8 +3,8 @@
"private": true,
"files": [],
"devDependencies": {
"@babel/core": "7.19.3",
"@babel/preset-env": "7.19.3",
"@babel/core": "7.19.6",
"@babel/preset-env": "7.19.4",
"@fortawesome/fontawesome-free": "6.2.0",
"@lodder/grunt-postcss": "3.1.1",
"autoprefixer": "10.4.12",
......@@ -27,7 +27,7 @@
"kleur": "4.1.5",
"load-grunt-tasks": "5.1.0",
"marked": "4.1.1",
"postcss": "8.4.17",
"postcss": "8.4.18",
"sass": "1.55.0",
"serve-static": "1.15.0"
},
......
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`docs/rules/area-alt.md inline validation: correct 1`] = `[]`;
exports[`docs/rules/area-alt.md inline validation: disabled-a11y 1`] = `[]`;
exports[`docs/rules/area-alt.md inline validation: enabled-a11y 1`] = `
[
{
"errorCount": 1,
"filePath": "inline",
"messages": [
{
"column": 27,
"context": "missing-alt",
"line": 3,
"message": ""alt" attribute must be set and non-empty when the "href" attribute is present",
"offset": 112,
"ruleId": "area-alt",
"ruleUrl": "https://html-validate.org/rules/area-alt.html",
"selector": "map > area:nth-child(1)",
"severity": 2,
"size": 3,
},
],
"source": "<img src="image.png" usemap="#imagemap" alt="An awesome image">
<map name="imagemap">
<area href="target.html" alt="">
<area href="target.html" alt="Link purpose">
</map>",
"warningCount": 0,
},
]
`;
exports[`docs/rules/area-alt.md inline validation: incorrect 1`] = `
[
{
"errorCount": 2,
"filePath": "inline",
"messages": [
{
"column": 8,
"context": "missing-alt",
"line": 3,
"message": ""alt" attribute must be set and non-empty when the "href" attribute is present",
"offset": 93,
"ruleId": "area-alt",
"ruleUrl": "https://html-validate.org/rules/area-alt.html",
"selector": "map > area:nth-child(1)",
"severity": 2,
"size": 4,
},
{
"column": 8,
"context": "missing-href",
"line": 4,
"message": ""alt" attribute cannot be used unless the "href" attribute is present",
"offset": 121,
"ruleId": "area-alt",
"ruleUrl": "https://html-validate.org/rules/area-alt.html",
"selector": "map > area:nth-child(2)",
"severity": 2,
"size": 3,
},
],
"source": "<img src="image.png" usemap="#imagemap" alt="An awesome image">
<map name="imagemap">
<area href="target1.html">
<area alt="Link purpose">
</map>",
"warningCount": 0,
},
]
`;
......@@ -57,7 +57,6 @@ exports[`docs/rules/attribute-allowed-values.md inline validation: incorrect 1`]
"url",
"week",
],
"required": true,
},
"attribute": "type",
"element": "input",
......
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`docs/rules/attribute-misuse.md inline validation: correct 1`] = `[]`;
exports[`docs/rules/attribute-misuse.md inline validation: incorrect 1`] = `
[
{
"errorCount": 4,
"filePath": "inline",
"messages": [
{
"column": 4,
"context": {
"attr": "target",
"details": "requires "href" attribute to be present",
},
"line": 1,
"message": ""target" attribute cannot be used in this context: requires "href" attribute to be present",
"offset": 3,
"ruleId": "attribute-misuse",
"ruleUrl": "https://html-validate.org/rules/attribute-misuse.html",
"selector": "a",
"severity": 2,
"size": 6,
},
{
"column": 23,
"context": {
"attr": "formaction",
"details": ""type" attribute must be "submit"",
},
"line": 2,
"message": ""formaction" attribute cannot be used in this context: "type" attribute must be "submit"",
"offset": 42,
"ruleId": "attribute-misuse",
"ruleUrl": "https://html-validate.org/rules/attribute-misuse.html",
"selector": "a > button",
"severity": 2,
"size": 10,
},
{
"column": 7,
"context": {
"attr": "name",
"details": "cannot be used at the same time as "http-equiv"",
},
"line": 3,
"message": ""name" attribute cannot be used in this context: cannot be used at the same time as "http-equiv"",
"offset": 67,
"ruleId": "attribute-misuse",
"ruleUrl": "https://html-validate.org/rules/attribute-misuse.html",
"selector": "a > button > meta",
"severity": 2,
"size": 4,
},
{
"column": 17,
"context": {
"attr": "http-equiv",
"details": "cannot be used at the same time as "name"",
},
"line": 3,
"message": ""http-equiv" attribute cannot be used in this context: cannot be used at the same time as "name"",
"offset": 77,
"ruleId": "attribute-misuse",
"ruleUrl": "https://html-validate.org/rules/attribute-misuse.html",
"selector": "a > button > meta",
"severity": 2,
"size": 10,
},
],
"source": "<a target="_blank">
<button type="button" formaction="post">
<meta name=".." http-equiv="..">",
"warningCount": 0,
},
]
`;
import HtmlValidate from "../../../src/htmlvalidate";
const markup: { [key: string]: string } = {};
markup["incorrect"] = `<img src="image.png" usemap="#imagemap" alt="An awesome image">
<map name="imagemap">
<area href="target1.html">
<area alt="Link purpose">
</map>`;
markup["correct"] = `<img src="image.png" usemap="#imagemap" alt="An awesome image">
<map name="imagemap">
<area href="target1.html" alt="Link purpose">
<area href="target2.html" alt="Link purpose">
</map>`;
markup["enabled-a11y"] = `<img src="image.png" usemap="#imagemap" alt="An awesome image">
<map name="imagemap">
<area href="target.html" alt="">
<area href="target.html" alt="Link purpose">
</map>`;
markup["disabled-a11y"] = `<img src="image.png" usemap="#imagemap" alt="An awesome image">
<map name="imagemap">
<area href="target.html" alt="">
<area href="target.html" alt="Link purpose">
</map>`;
describe("docs/rules/area-alt.md", () => {
it("inline validation: incorrect", () => {
expect.assertions(1);
const htmlvalidate = new HtmlValidate({"rules":{"area-alt":"error"}});
const report = htmlvalidate.validateString(markup["incorrect"]);
expect(report.results).toMatchSnapshot();
});
it("inline validation: correct", () => {
expect.assertions(1);
const htmlvalidate = new HtmlValidate({"rules":{"area-alt":"error"}});
const report = htmlvalidate.validateString(markup["correct"]);
expect(report.results).toMatchSnapshot();
});
it("inline validation: enabled-a11y", () => {
expect.assertions(1);
const htmlvalidate = new HtmlValidate({"rules":{"area-alt":["error",{"accessible":true}]}});
const report = htmlvalidate.validateString(markup["enabled-a11y"]);
expect(report.results).toMatchSnapshot();
});
it("inline validation: disabled-a11y", () => {
expect.assertions(1);
const htmlvalidate = new HtmlValidate({"rules":{"area-alt":["error",{"accessible":false}]}});
const report = htmlvalidate.validateString(markup["disabled-a11y"]);
expect(report.results).toMatchSnapshot();
});
});
import HtmlValidate from "../../../src/htmlvalidate";
const markup: { [key: string]: string } = {};
markup["incorrect"] = `<a target="_blank">
<button type="button" formaction="post">
<meta name=".." http-equiv="..">`;
markup["correct"] = `<a href=".." target="_blank">
<button type="submit" formaction="post">
<meta name=".." content="..">
<meta http-equiv=".." content="..">`;
describe("docs/rules/attribute-misuse.md", () => {
it("inline validation: incorrect", () => {
expect.assertions(1);
const htmlvalidate = new HtmlValidate({"rules":{"attribute-misuse":"error"}});
const report = htmlvalidate.validateString(markup["incorrect"]);
expect(report.results).toMatchSnapshot();
});
it("inline validation: correct", () => {
expect.assertions(1);
const htmlvalidate = new HtmlValidate({"rules":{"attribute-misuse":"error"}});
const report = htmlvalidate.validateString(markup["correct"]);
expect(report.results).toMatchSnapshot();
});
});
---
docType: rule
name: area-alt
category: a11y
summary: Require alternative text on `<area>` elements
---
# Require alternative text on `<area>` elements (`area-alt`)
The `alt` attribute is used to provide an alternative text describing the area in the image map.
It is used both by the browser when the `<img>` source is missing but most of all it is used by screen readers and is a required technique for [WCAG 2.1 H24][wcag-h24].
[wcag-h24]: https://www.w3.org/WAI/WCAG21/Techniques/html/H24
## Rule details
Examples of **incorrect** code for this rule:
<validate name="incorrect" rules="area-alt">
<img src="image.png" usemap="#imagemap" alt="An awesome image">
<map name="imagemap">
<area href="target1.html">
<area alt="Link purpose">
</map>
</validate>
Examples of **correct** code for this rule:
<validate name="correct" rules="area-alt">
<img src="image.png" usemap="#imagemap" alt="An awesome image">
<map name="imagemap">
<area href="target1.html" alt="Link purpose">
<area href="target2.html" alt="Link purpose">
</map>
</validate>
## Options
This rule takes an optional object:
```json
{
"accessible": true
}
```
### `accessible`
The HTML5 standard allows only a single `<area>` element to contain alternative text when a group of `<area>` references the same resource.
WCAG requires all of them to be labeled even if referencing the same resource.
This is because HTML only considers the case when the image is missing and uses an algorithm to remove duplicated links pointing to the same resource, thus only requiring a single `<area>` for each resource to contain the `alt` text.
As for screen readers and accessibility each of the `<area>` elements could be read by the user and thus each of them must be adequate labeled.
This option is enabled by default by the `html-validate:recommended` and `html-validate:a11y` presets but disabled by the `html-validate:standard` preset.
With this option **enabled** the following is **incorrect**:
<validate name="enabled-a11y" rules="area-alt" area-alt='{ "accessible": true }'>
<img src="image.png" usemap="#imagemap" alt="An awesome image">
<map name="imagemap">
<area href="target.html" alt="">
<area href="target.html" alt="Link purpose">
</map>
</validate>
With this option **disabled** the following is **correct**:
<validate name="disabled-a11y" rules="area-alt" area-alt='{ "accessible": false }'>
<img src="image.png" usemap="#imagemap" alt="An awesome image">
<map name="imagemap">
<area href="target.html" alt="">
<area href="target.html" alt="Link purpose">
</map>
</validate>
## Version history
- 7.7.0 - Rule added.
---
docType: rule
name: attribute-misuse
category: content-model
summary: Require attribute to be used in correct context
---
# Require attribute to be used in correct context (`attribute-misuse`)
Some attributes have usage requirements, for instance:
- `<a target>` requires the `href` attribute.
- `<button formaction>` requires `type="submit"`.
- `<meta content>` requires one of the `name`, `http-equiv` or `itemprop` attributes.
- `<meta>` can only contain of of the `name`, `http-equiv` or `itemprop` attributes.
- `<meta name>` requires the `content` attribute.
## Rule details
Examples of **incorrect** code for this rule:
<validate name="incorrect" rules="attribute-misuse">
<a target="_blank">
<button type="button" formaction="post">
<meta name=".." http-equiv="..">
</validate>
Examples of **correct** code for this rule:
<validate name="correct" rules="attribute-misuse">
<a href=".." target="_blank">
<button type="submit" formaction="post">
<meta name=".." content="..">
<meta http-equiv=".." content="..">
</validate>
## Version history
- 7.7.0 - Rule added.
......@@ -4,7 +4,7 @@ name: no-dup-id
summary: Disallow duplicated IDs
---
# Disallows elements width duplicated ID (`no-dup-id`)
# Disallows elements with duplicated ID (`no-dup-id`)
The ID of an element [must be unique](https://www.w3.org/TR/html5/dom.html#the-id-attribute).
......
......@@ -17,7 +17,6 @@ exports[`docs/usage/index.md inline validation: disable-block-button-type 1`] =
"reset",
"button",
],
"required": true,
},
"attribute": "type",
"element": "button",
......@@ -41,7 +40,6 @@ exports[`docs/usage/index.md inline validation: disable-block-button-type 1`] =
"reset",
"button",
],
"required": true,
},
"attribute": "type",
"element": "button",
......
......@@ -5,19 +5,24 @@ title: Elements metadata
# Elements metadata
For proper validation each element must have a corresponding metadata entry. If
no metadata is present many rules will just ignore the element entirely. To
configure metadata sources use `elements: [...]`, see
[configuring](/usage).
For proper validation each element must have a corresponding metadata entry.
If no metadata is present many rules will just ignore the element entirely.
To configure metadata sources use `elements: [...]`, see [configuring](/usage).
A typical custom element may look like:
```js
"custom-element": {
"flow": true
}
const { defineMetadata } = require("html-validate");
module.exports = defineMetadata({
"custom-element": {
flow: true,
},
});
```
The `defineMetadata` helper is optional but helps the IDE with completion and type-checking.
Each entry can contain the following properties:
```typescript
......@@ -194,6 +199,7 @@ An object with allowed attribute values.
```typescript
export interface MetaAttribute {
allowed?: (node: HtmlElement) => string | null;
boolean?: boolean;
deprecated?: boolean | string;
enum?: Array<string | RegExp>;
......@@ -203,18 +209,20 @@ export interface MetaAttribute {
}
```
```json
{
```js
const { defineMetadata } = require("html-validate");
module.exports = defineMetadata({
"custom-element": {
"attributes": {
"foo": {
"boolean": false,
"omit": false,
"enum": ["bar", "baz"]
}
}
}
}
attributes: {
foo: {
boolean: false,
omit: false,
enum: ["bar", "baz"],
},
},
},
});
```
With this metadata the attribute `"foo"` may only have the values `"bar"` or`"foo"`.
......@@ -224,6 +232,35 @@ This is used by the {@link attribute-allowed-values} rule.
An empty object may be set as well to mark the attribute as a known attribute but without any validation.
#### `attribute.allowed`
The `allowed` property can be set to a callback taking a single element.
If the callback returns an error string the attribute cannot be used in the given context.
```js
const { defineMetadata } = require("html-validate");
module.exports = defineMetadata({
"custom-element": {
attributes: {
foo: {
allowed(node) {
if (!node.hasAttribute("bar")) {
return "needs a bar attribute";
} else {
return null;
}
},
},
},
},
});
```
Helper functions for writing callbacks are available in {@link api:MetadataHelper}.
This is used by the {@link attribute-misuse} rule to check if an attribute is allowed or not in the context.
#### `attribute.enum`
The `enum` property is a list of allowed values the attribute can have.
......
......@@ -32,8 +32,11 @@ textual description of the content. E.g. it cannot suggest to use `<abbr>` or
</tr>
<tr>
<td class="table-right">H24</td>
<td>Providing text alternatives for the area elements of image maps</td>
<td class="support-planned">Planned</td>
<td>
Providing text alternatives for the area elements of image maps.
<em> Use {@link rule:area-alt} to validate. Only checks for presence of text. </em>
</td>
<td class="support-yes">Yes</td>
</tr>
<tr>
<td class="table-right">H25</td>
......