@markuplint/ml-spec 4.10.0 → 4.10.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.ja.md +253 -0
- package/ARCHITECTURE.md +253 -0
- package/CHANGELOG.md +7 -4
- package/README.md +4 -186
- package/SKILL.md +116 -0
- package/docs/aria-algorithms.ja.md +651 -0
- package/docs/aria-algorithms.md +651 -0
- package/docs/html-algorithms.ja.md +469 -0
- package/docs/html-algorithms.md +469 -0
- package/docs/maintenance.ja.md +340 -0
- package/docs/maintenance.md +340 -0
- package/docs/spec-resolution.ja.md +540 -0
- package/docs/spec-resolution.md +551 -0
- package/docs/type-definitions.ja.md +561 -0
- package/docs/type-definitions.md +561 -0
- package/lib/algorithm/aria/accname-computation.d.ts +7 -0
- package/lib/algorithm/aria/accname-computation.js +7 -0
- package/lib/algorithm/aria/aria-specs.d.ts +7 -0
- package/lib/algorithm/aria/aria-specs.js +7 -0
- package/lib/algorithm/aria/get-aria.d.ts +12 -0
- package/lib/algorithm/aria/get-aria.js +12 -0
- package/lib/algorithm/aria/get-computed-aria-props.d.ts +22 -0
- package/lib/algorithm/aria/get-computed-aria-props.js +10 -0
- package/lib/algorithm/aria/get-computed-role.d.ts +12 -0
- package/lib/algorithm/aria/get-computed-role.js +12 -0
- package/lib/algorithm/aria/get-implicit-role.d.ts +18 -0
- package/lib/algorithm/aria/get-implicit-role.js +18 -0
- package/lib/algorithm/aria/get-permitted-roles.d.ts +9 -0
- package/lib/algorithm/aria/get-permitted-roles.js +9 -0
- package/lib/algorithm/aria/get-role-spec.d.ts +11 -0
- package/lib/algorithm/aria/get-role-spec.js +11 -0
- package/lib/algorithm/aria/has-required-owned-elements.d.ts +23 -0
- package/lib/algorithm/aria/has-required-owned-elements.js +23 -0
- package/lib/algorithm/aria/is-exposed.d.ts +7 -4
- package/lib/algorithm/aria/is-exposed.js +7 -4
- package/lib/algorithm/aria/is-presentational.d.ts +8 -0
- package/lib/algorithm/aria/is-presentational.js +8 -0
- package/lib/algorithm/aria/matches-context-role.d.ts +11 -0
- package/lib/algorithm/aria/matches-context-role.js +11 -0
- package/lib/algorithm/html/content-model-category-to-tag-names.d.ts +9 -0
- package/lib/algorithm/html/content-model-category-to-tag-names.js +9 -0
- package/lib/algorithm/html/get-content-model.d.ts +9 -0
- package/lib/algorithm/html/get-content-model.js +9 -0
- package/lib/algorithm/html/get-selectors-by-content-model-category.d.ts +8 -0
- package/lib/algorithm/html/get-selectors-by-content-model-category.js +8 -0
- package/lib/algorithm/html/is-nothing-content-model.d.ts +7 -0
- package/lib/algorithm/html/is-nothing-content-model.js +7 -0
- package/lib/algorithm/html/is-palpable-elements.d.ts +13 -0
- package/lib/algorithm/html/is-palpable-elements.js +13 -0
- package/lib/algorithm/html/is-void-element.d.ts +9 -0
- package/lib/algorithm/html/is-void-element.js +9 -0
- package/lib/algorithm/html/may-be-focusable.d.ts +10 -0
- package/lib/algorithm/html/may-be-focusable.js +10 -0
- package/lib/types/index.d.ts +54 -0
- package/lib/utils/aria-version.d.ts +6 -0
- package/lib/utils/aria-version.js +6 -0
- package/lib/utils/get-attr-specs-spec.d.ts +18 -0
- package/lib/utils/get-attr-specs-spec.js +18 -0
- package/lib/utils/get-attr-specs.d.ts +9 -0
- package/lib/utils/get-attr-specs.js +9 -0
- package/lib/utils/get-spec-by-tag-name.d.ts +11 -0
- package/lib/utils/get-spec-by-tag-name.js +11 -0
- package/lib/utils/get-spec.d.ts +11 -1
- package/lib/utils/get-spec.js +10 -0
- package/lib/utils/resolve-namespace.d.ts +13 -0
- package/lib/utils/resolve-namespace.js +10 -0
- package/lib/utils/schema-to-spec.d.ts +5 -2
- package/lib/utils/schema-to-spec.js +5 -2
- package/lib/utils/validate-aria-version.d.ts +7 -0
- package/lib/utils/validate-aria-version.js +7 -0
- package/package.json +6 -6
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
# HTML Content Model Algorithms
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The `@markuplint/ml-spec` package implements content model evaluation and element classification algorithms derived from the [HTML Living Standard](https://html.spec.whatwg.org/multipage/dom.html#content-models). These functions operate on the DOM `Element` interface together with `MLMLSpec` (the markuplint specification data) to determine what content an element may contain, whether an element is void, palpable, focusable, and more.
|
|
6
|
+
|
|
7
|
+
All HTML algorithm functions are located under `src/algorithm/html/`.
|
|
8
|
+
|
|
9
|
+
The content model category types, defined in `src/types/permitted-structures.ts`, cover **10 HTML categories** and **19 SVG categories**, forming the foundation of the content model system.
|
|
10
|
+
|
|
11
|
+
## Content Model System
|
|
12
|
+
|
|
13
|
+
### Category-to-Selector Mapping
|
|
14
|
+
|
|
15
|
+
The spec definitions (`SpecDefs['#contentModels']`) map each `Category` string to a readonly array of CSS selector strings:
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
type SpecDefs = {
|
|
19
|
+
readonly '#contentModels': {
|
|
20
|
+
readonly [model in Category]?: readonly string[];
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Each category maps to selectors that identify which elements belong to that category. For example:
|
|
26
|
+
|
|
27
|
+
- `#flow` maps to `['a', 'abbr', 'address', ...]`
|
|
28
|
+
- `#interactive` maps to `['a[href]', 'audio[controls]', ...]`
|
|
29
|
+
|
|
30
|
+
Note that some selectors include attribute conditions (`a[href]`, `audio[controls]`), meaning an element's membership in a category can depend on its attributes. This is how the HTML Standard's conditional content models are represented.
|
|
31
|
+
|
|
32
|
+
### Conditional Content Models
|
|
33
|
+
|
|
34
|
+
An element's permitted content can vary based on its attributes. The `ContentModel` interface supports this:
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
interface ContentModel {
|
|
38
|
+
contents: PermittedContentPattern[] | boolean;
|
|
39
|
+
descendantOf?: string;
|
|
40
|
+
conditional?: {
|
|
41
|
+
condition: string;
|
|
42
|
+
contents: PermittedContentPattern[] | boolean;
|
|
43
|
+
}[];
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
When `conditional` is present, each condition's `condition` string is tested via `el.matches(condition)`. The first matching condition's `contents` value is used. If no condition matches, the default `contents` value applies.
|
|
48
|
+
|
|
49
|
+
## Function Reference
|
|
50
|
+
|
|
51
|
+
### 1. `getContentModel(el, specs)`
|
|
52
|
+
|
|
53
|
+
**File:** `src/algorithm/html/get-content-model.ts`
|
|
54
|
+
|
|
55
|
+
Retrieves the permitted content model for an element.
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
function getContentModel(
|
|
59
|
+
el: Element,
|
|
60
|
+
specs: readonly Pick<ElementSpec, 'name' | 'contentModel'>[],
|
|
61
|
+
): ReadonlyDeep<PermittedContentPattern[]> | boolean | null;
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**Parameters:**
|
|
65
|
+
|
|
66
|
+
| Parameter | Type | Description |
|
|
67
|
+
| --------- | -------------------------------------------------------- | ----------------------------------------------------------- |
|
|
68
|
+
| `el` | `Element` | The DOM element to retrieve the content model for |
|
|
69
|
+
| `specs` | `readonly Pick<ElementSpec, 'name' \| 'contentModel'>[]` | Element specifications containing content model definitions |
|
|
70
|
+
|
|
71
|
+
**Return value:**
|
|
72
|
+
|
|
73
|
+
| Value | Meaning |
|
|
74
|
+
| --------------------------- | -------------------------------------- |
|
|
75
|
+
| `PermittedContentPattern[]` | Specific content rules for the element |
|
|
76
|
+
| `true` | Any content is permitted |
|
|
77
|
+
| `false` | No content is permitted |
|
|
78
|
+
| `null` | No specification found for the element |
|
|
79
|
+
|
|
80
|
+
**Behavior:**
|
|
81
|
+
|
|
82
|
+
1. Checks the nested `Map<Specs, Map<Element, result>>` cache. If a cached result exists for the given specs reference and element instance, returns it immediately.
|
|
83
|
+
2. Looks up the element's spec using `getSpec()`. If not found, caches and returns `null`.
|
|
84
|
+
3. Iterates over `contentModel.conditional[]` (if present). For each condition, calls `el.matches(cond.condition)`.
|
|
85
|
+
4. Returns the first matching condition's `contents`. If no condition matches, returns the default `contentModel.contents`.
|
|
86
|
+
5. All results are cached before being returned.
|
|
87
|
+
|
|
88
|
+
**Caching strategy:**
|
|
89
|
+
|
|
90
|
+
The cache is a two-level `Map`: the outer map is keyed by the `specs` array reference, and the inner map is keyed by the `Element` instance. This ensures results are correctly invalidated when specs change while avoiding redundant computation for the same element.
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
### 2. `isPalpableElement(el, specs, options?)`
|
|
95
|
+
|
|
96
|
+
**File:** `src/algorithm/html/is-palpable-elements.ts`
|
|
97
|
+
|
|
98
|
+
Determines whether an element is considered palpable content -- elements that render something visible or meaningful to the user.
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
function isPalpableElement(
|
|
102
|
+
el: Element,
|
|
103
|
+
specs: MLMLSpec,
|
|
104
|
+
options?: {
|
|
105
|
+
readonly extendsSvg?: boolean;
|
|
106
|
+
readonly extendsExposableElements?: boolean;
|
|
107
|
+
},
|
|
108
|
+
): boolean;
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**Parameters:**
|
|
112
|
+
|
|
113
|
+
| Parameter | Type | Default | Description |
|
|
114
|
+
| ---------------------------------- | ---------- | ------- | -------------------------------------- |
|
|
115
|
+
| `el` | `Element` | -- | The DOM element to check |
|
|
116
|
+
| `specs` | `MLMLSpec` | -- | The full markup language specification |
|
|
117
|
+
| `options.extendsSvg` | `boolean` | `true` | Include `#SVGRenderable` elements |
|
|
118
|
+
| `options.extendsExposableElements` | `boolean` | `false` | Include additional exposable elements |
|
|
119
|
+
|
|
120
|
+
**Exposable elements** (elements that are semantically meaningful but do not belong to the `#palpable` category):
|
|
121
|
+
|
|
122
|
+
`body`, `dd`, `dt`, `figcaption`, `html`, `legend`, `li`, `optgroup`, `option`, `rp`, `rt`, `summary`, `tbody`, `td`, `tfoot`, `th`, `thead`, `tr`
|
|
123
|
+
|
|
124
|
+
**Behavior:**
|
|
125
|
+
|
|
126
|
+
1. Collects CSS selectors from the `#palpable` content model category.
|
|
127
|
+
2. If `extendsSvg` is not `false` (default: `true`), appends `#SVGRenderable` selectors.
|
|
128
|
+
3. If `extendsExposableElements` is `true` (default: `false`), appends the hardcoded list of exposable elements.
|
|
129
|
+
4. Returns `true` if `el.matches()` succeeds against any of the collected selector strings.
|
|
130
|
+
|
|
131
|
+
> **Warning:** This implementation involves the author's interpretation of the HTML specification. If you find inaccuracies, please file an issue at https://github.com/markuplint/markuplint/issues/new.
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
### 3. `isVoidElement(el)`
|
|
136
|
+
|
|
137
|
+
**File:** `src/algorithm/html/is-void-element.ts`
|
|
138
|
+
|
|
139
|
+
Checks whether an element is a void element as defined by the HTML specification. Void elements cannot have any contents.
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
function isVoidElement(el: { readonly localName: string }): boolean;
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**Parameter:**
|
|
146
|
+
|
|
147
|
+
| Parameter | Type | Description |
|
|
148
|
+
| --------- | -------------------------------- | -------------------------------------- |
|
|
149
|
+
| `el` | `{ readonly localName: string }` | Any object with a `localName` property |
|
|
150
|
+
|
|
151
|
+
Note that this function accepts any object with a `localName` property, not just a DOM `Element`. This makes it usable in contexts where a full Element is not available.
|
|
152
|
+
|
|
153
|
+
**Void elements (13):**
|
|
154
|
+
|
|
155
|
+
`area`, `base`, `br`, `col`, `embed`, `hr`, `img`, `input`, `link`, `meta`, `source`, `track`, `wbr`
|
|
156
|
+
|
|
157
|
+
The list is stored as a `Set` for O(1) lookup performance.
|
|
158
|
+
|
|
159
|
+
**Spec:** https://html.spec.whatwg.org/multipage/syntax.html#void-elements
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
### 4. `isNothingContentModel(el)`
|
|
164
|
+
|
|
165
|
+
**File:** `src/algorithm/html/is-nothing-content-model.ts`
|
|
166
|
+
|
|
167
|
+
Determines whether an element uses the "nothing" content model, meaning it must not contain any content.
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
function isNothingContentModel(el: Element): boolean;
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
**Parameter:**
|
|
174
|
+
|
|
175
|
+
| Parameter | Type | Description |
|
|
176
|
+
| --------- | --------- | ------------------------ |
|
|
177
|
+
| `el` | `Element` | The DOM element to check |
|
|
178
|
+
|
|
179
|
+
**Behavior:**
|
|
180
|
+
|
|
181
|
+
Returns `true` if the element is:
|
|
182
|
+
|
|
183
|
+
- A **void element** (delegates to `isVoidElement()`), or
|
|
184
|
+
- An `<iframe>` element, or
|
|
185
|
+
- A `<template>` element
|
|
186
|
+
|
|
187
|
+
While `<iframe>` and `<template>` are not void elements, they also use the "nothing" content model per the HTML specification -- their content is either replaced (iframe) or stored in a separate document fragment (template).
|
|
188
|
+
|
|
189
|
+
**Spec:** https://html.spec.whatwg.org/multipage/dom.html#the-nothing-content-model
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
### 5. `mayBeFocusable(el, specs)`
|
|
194
|
+
|
|
195
|
+
**File:** `src/algorithm/html/may-be-focusable.ts`
|
|
196
|
+
|
|
197
|
+
Performs a heuristic check to determine whether an element may potentially be focusable.
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
function mayBeFocusable(el: Element, specs: MLMLSpec): boolean;
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
**Parameters:**
|
|
204
|
+
|
|
205
|
+
| Parameter | Type | Description |
|
|
206
|
+
| --------- | ---------- | -------------------------------------- |
|
|
207
|
+
| `el` | `Element` | The DOM element to check |
|
|
208
|
+
| `specs` | `MLMLSpec` | The full markup language specification |
|
|
209
|
+
|
|
210
|
+
**Behavior:**
|
|
211
|
+
|
|
212
|
+
Matches the element against the following selectors:
|
|
213
|
+
|
|
214
|
+
1. All selectors from the `#interactive` content model category (retrieved via `getSelectorsByContentModelCategory()`)
|
|
215
|
+
2. `[tabindex]` -- any element with a `tabindex` attribute
|
|
216
|
+
3. `[contenteditable]:not([contenteditable="false" i])` -- contenteditable elements (case-insensitive comparison)
|
|
217
|
+
|
|
218
|
+
Returns `true` if `el.matches()` succeeds against any of these selectors.
|
|
219
|
+
|
|
220
|
+
**Limitations:**
|
|
221
|
+
|
|
222
|
+
This is a static heuristic. It does **not** account for runtime state that could prevent focusability:
|
|
223
|
+
|
|
224
|
+
- `disabled` attribute on form elements
|
|
225
|
+
- `inert` attribute
|
|
226
|
+
- Elements hidden via CSS (`display: none`, `visibility: hidden`)
|
|
227
|
+
- Shadow DOM boundaries
|
|
228
|
+
|
|
229
|
+
This function is primarily used by ARIA role computation to prevent presentational roles (`role="none"` / `role="presentation"`) from being applied to focusable elements, as per the WAI-ARIA specification.
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
### 6. `getSelectorsByContentModelCategory(specs, category)`
|
|
234
|
+
|
|
235
|
+
**File:** `src/algorithm/html/get-selectors-by-content-model-category.ts`
|
|
236
|
+
|
|
237
|
+
Direct accessor to retrieve the CSS selectors associated with a content model category.
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
function getSelectorsByContentModelCategory(specs: MLMLSpec, category: Category): ReadonlyArray<string>;
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
**Parameters:**
|
|
244
|
+
|
|
245
|
+
| Parameter | Type | Description |
|
|
246
|
+
| ---------- | ---------- | ---------------------------------------------------------- |
|
|
247
|
+
| `specs` | `MLMLSpec` | The full markup language specification |
|
|
248
|
+
| `category` | `Category` | The content model category (e.g., `#flow`, `#interactive`) |
|
|
249
|
+
|
|
250
|
+
**Return value:**
|
|
251
|
+
|
|
252
|
+
A readonly array of CSS selector strings for the category, or an empty array if the category is not defined in the spec.
|
|
253
|
+
|
|
254
|
+
**Implementation:**
|
|
255
|
+
|
|
256
|
+
```typescript
|
|
257
|
+
const selectors = specs.def['#contentModels'][category];
|
|
258
|
+
return selectors ?? [];
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
This is a thin wrapper that provides null safety over direct property access.
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
### 7. `contentModelCategoryToTagNames(contentModel, def)`
|
|
266
|
+
|
|
267
|
+
**File:** `src/algorithm/html/content-model-category-to-tag-names.ts`
|
|
268
|
+
|
|
269
|
+
Converts a content model category to a sorted, frozen array of tag names belonging to that category.
|
|
270
|
+
|
|
271
|
+
```typescript
|
|
272
|
+
function contentModelCategoryToTagNames(contentModel: Category, def: MLMLSpec['def']): ReadonlyArray<string>;
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
**Parameters:**
|
|
276
|
+
|
|
277
|
+
| Parameter | Type | Description |
|
|
278
|
+
| -------------- | ----------------- | ------------------------------------- |
|
|
279
|
+
| `contentModel` | `Category` | The content model category identifier |
|
|
280
|
+
| `def` | `MLMLSpec['def']` | The specification definitions |
|
|
281
|
+
|
|
282
|
+
**Return value:**
|
|
283
|
+
|
|
284
|
+
A `Object.freeze()`-d, sorted array of tag name strings. Returns a frozen empty array if the category is not defined or has no entries.
|
|
285
|
+
|
|
286
|
+
**Caching:**
|
|
287
|
+
|
|
288
|
+
Results are cached in a module-level `Map<Category, ReadonlyArray<string>>`. Once computed for a given category, the result is reused on subsequent calls.
|
|
289
|
+
|
|
290
|
+
**Important note:** This function extracts tag names directly from the selector strings stored in the content model definitions. It does not perform CSS selector parsing -- the selectors in `#contentModels` are expected to be simple tag names or tag-with-attribute selectors. Complex selectors may not be correctly decomposed into tag names.
|
|
291
|
+
|
|
292
|
+
## Content Model Categories
|
|
293
|
+
|
|
294
|
+
### HTML Categories (10)
|
|
295
|
+
|
|
296
|
+
| Category | Description | Example Elements |
|
|
297
|
+
| -------------------- | ------------------------------------- | ---------------------------------------------------- |
|
|
298
|
+
| `#text` | Text content | Text nodes |
|
|
299
|
+
| `#phrasing` | Inline-level content | `a`, `em`, `strong`, `span`, `img` |
|
|
300
|
+
| `#flow` | Block-level and inline content | Nearly all body elements |
|
|
301
|
+
| `#interactive` | User-interactable content | `a[href]`, `button`, `input`, `select` |
|
|
302
|
+
| `#heading` | Section headings | `h1`, `h2`, `h3`, `h4`, `h5`, `h6` |
|
|
303
|
+
| `#sectioning` | Document structure sections | `article`, `aside`, `nav`, `section` |
|
|
304
|
+
| `#metadata` | Metadata about the document | `base`, `link`, `meta`, `script`, `style`, `title` |
|
|
305
|
+
| `#embedded` | External content embedded in document | `audio`, `canvas`, `embed`, `iframe`, `img`, `video` |
|
|
306
|
+
| `#palpable` | Visible/meaningful content | Most flow/phrasing elements except metadata |
|
|
307
|
+
| `#script-supporting` | Script infrastructure | `script`, `template` |
|
|
308
|
+
|
|
309
|
+
### SVG Categories (19)
|
|
310
|
+
|
|
311
|
+
| Category | Description |
|
|
312
|
+
| -------------------------- | -------------------------------------------------------------------------------------- |
|
|
313
|
+
| `#SVGAnimation` | SVG animation elements (`animate`, `animateMotion`, etc.) |
|
|
314
|
+
| `#SVGBasicShapes` | Basic shape elements (`circle`, `ellipse`, `line`, `polygon`, `polyline`, `rect`) |
|
|
315
|
+
| `#SVGContainer` | Container elements (`a`, `defs`, `g`, `marker`, `mask`, `svg`, `symbol`, etc.) |
|
|
316
|
+
| `#SVGDescriptive` | Descriptive elements (`desc`, `metadata`, `title`) |
|
|
317
|
+
| `#SVGFilterPrimitive` | Filter primitive elements (`feBlend`, `feColorMatrix`, `feGaussianBlur`, etc.) |
|
|
318
|
+
| `#SVGFont` | Font elements (deprecated: `font`, `font-face`, etc.) |
|
|
319
|
+
| `#SVGGradient` | Gradient elements (`linearGradient`, `radialGradient`, `stop`) |
|
|
320
|
+
| `#SVGGraphics` | Graphics elements (shapes, images, text, etc.) |
|
|
321
|
+
| `#SVGGraphicsReferencing` | Graphics referencing elements (`image`, `use`) |
|
|
322
|
+
| `#SVGLightSource` | Light source elements (`feDistantLight`, `fePointLight`, `feSpotLight`) |
|
|
323
|
+
| `#SVGNeverRendered` | Elements that are never rendered directly (`clipPath`, `defs`, `linearGradient`, etc.) |
|
|
324
|
+
| `#SVGNone` | No content permitted |
|
|
325
|
+
| `#SVGPaintServer` | Paint server elements (`linearGradient`, `pattern`, `radialGradient`, etc.) |
|
|
326
|
+
| `#SVGRenderable` | Elements that can be rendered (`a`, `circle`, `g`, `rect`, `svg`, `text`, etc.) |
|
|
327
|
+
| `#SVGShape` | Shape elements (`circle`, `ellipse`, `line`, `path`, `polygon`, `polyline`, `rect`) |
|
|
328
|
+
| `#SVGStructural` | Structural elements (`defs`, `g`, `svg`, `symbol`, `use`) |
|
|
329
|
+
| `#SVGStructurallyExternal` | Structurally external elements (`image`, `use`) |
|
|
330
|
+
| `#SVGTextContent` | Text content elements (`text`, `textPath`, `tspan`) |
|
|
331
|
+
| `#SVGTextContentChild` | Text content child elements (`textPath`, `tspan`) |
|
|
332
|
+
|
|
333
|
+
## PermittedContentPattern Format
|
|
334
|
+
|
|
335
|
+
The `PermittedContentPattern` type is a discriminated union of six pattern types. These patterns are defined in `src/types/permitted-structures.ts` and are auto-generated from a JSON Schema.
|
|
336
|
+
|
|
337
|
+
### Pattern Types
|
|
338
|
+
|
|
339
|
+
#### `PermittedContentRequire`
|
|
340
|
+
|
|
341
|
+
Specifies required content that must appear.
|
|
342
|
+
|
|
343
|
+
```typescript
|
|
344
|
+
interface PermittedContentRequire {
|
|
345
|
+
require: Model | PermittedContentPattern[];
|
|
346
|
+
min?: number;
|
|
347
|
+
max?: number;
|
|
348
|
+
}
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
**Example:** An element that requires exactly one `<caption>`:
|
|
352
|
+
|
|
353
|
+
```json
|
|
354
|
+
{ "require": "caption", "min": 1, "max": 1 }
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
#### `PermittedContentOptional`
|
|
358
|
+
|
|
359
|
+
Specifies content that may optionally appear.
|
|
360
|
+
|
|
361
|
+
```typescript
|
|
362
|
+
interface PermittedContentOptional {
|
|
363
|
+
optional: Model | PermittedContentPattern[];
|
|
364
|
+
max?: number;
|
|
365
|
+
}
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
**Example:** An optional `<thead>`:
|
|
369
|
+
|
|
370
|
+
```json
|
|
371
|
+
{ "optional": "thead", "max": 1 }
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
#### `PermittedContentOneOrMore`
|
|
375
|
+
|
|
376
|
+
Specifies content that must appear at least once (1..N).
|
|
377
|
+
|
|
378
|
+
```typescript
|
|
379
|
+
interface PermittedContentOneOrMore {
|
|
380
|
+
oneOrMore: Model | PermittedContentPattern[];
|
|
381
|
+
max?: number;
|
|
382
|
+
}
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
**Example:** One or more `<tr>` elements:
|
|
386
|
+
|
|
387
|
+
```json
|
|
388
|
+
{ "oneOrMore": "tr" }
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
#### `PermittedContentZeroOrMore`
|
|
392
|
+
|
|
393
|
+
Specifies content that may appear any number of times (0..N).
|
|
394
|
+
|
|
395
|
+
```typescript
|
|
396
|
+
interface PermittedContentZeroOrMore {
|
|
397
|
+
zeroOrMore: Model | PermittedContentPattern[];
|
|
398
|
+
max?: number;
|
|
399
|
+
}
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
**Example:** Zero or more flow content children:
|
|
403
|
+
|
|
404
|
+
```json
|
|
405
|
+
{ "zeroOrMore": "#flow" }
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
#### `PermittedContentChoice`
|
|
409
|
+
|
|
410
|
+
Specifies a choice between two to five alternative content patterns.
|
|
411
|
+
|
|
412
|
+
```typescript
|
|
413
|
+
interface PermittedContentChoice {
|
|
414
|
+
choice:
|
|
415
|
+
| [PermittedContentPattern[], PermittedContentPattern[]]
|
|
416
|
+
| [PermittedContentPattern[], PermittedContentPattern[], PermittedContentPattern[]]
|
|
417
|
+
| [
|
|
418
|
+
/* 4 alternatives */
|
|
419
|
+
]
|
|
420
|
+
| [
|
|
421
|
+
/* 5 alternatives */
|
|
422
|
+
];
|
|
423
|
+
}
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
**Example:** Either flow content or `<param>` elements followed by flow content:
|
|
427
|
+
|
|
428
|
+
```json
|
|
429
|
+
{
|
|
430
|
+
"choice": [[{ "zeroOrMore": "#flow" }], [{ "oneOrMore": "param" }, { "zeroOrMore": "#flow" }]]
|
|
431
|
+
}
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
#### `PermittedContentTransparent`
|
|
435
|
+
|
|
436
|
+
Indicates that the element inherits its parent's content model (transparent content model).
|
|
437
|
+
|
|
438
|
+
```typescript
|
|
439
|
+
interface PermittedContentTransparent {
|
|
440
|
+
transparent: string;
|
|
441
|
+
}
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
**Example:** The `<a>` element is transparent:
|
|
445
|
+
|
|
446
|
+
```json
|
|
447
|
+
{ "transparent": "a" }
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
### The `Model` Type
|
|
451
|
+
|
|
452
|
+
The `require`, `optional`, `oneOrMore`, and `zeroOrMore` properties accept a `Model` value, which is defined as:
|
|
453
|
+
|
|
454
|
+
```typescript
|
|
455
|
+
type Model = ContentType | ContentType[];
|
|
456
|
+
type ContentType = string | Category;
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
- A single string: either a tag name (e.g., `"div"`) or a category (e.g., `"#flow"`)
|
|
460
|
+
- An array of strings: multiple permitted types (logical OR)
|
|
461
|
+
|
|
462
|
+
## HTML Standard References
|
|
463
|
+
|
|
464
|
+
- [Content models](https://html.spec.whatwg.org/multipage/dom.html#content-models)
|
|
465
|
+
- [Void elements](https://html.spec.whatwg.org/multipage/syntax.html#void-elements)
|
|
466
|
+
- [Interactive content](https://html.spec.whatwg.org/multipage/dom.html#interactive-content)
|
|
467
|
+
- [Palpable content](https://html.spec.whatwg.org/multipage/dom.html#palpable-content)
|
|
468
|
+
- [The nothing content model](https://html.spec.whatwg.org/multipage/dom.html#the-nothing-content-model)
|
|
469
|
+
- [Element index](https://html.spec.whatwg.org/multipage/indices.html#elements-3)
|