You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Documents the ~33 selector forms Folio's stylesheet parser recognizes:
4 combinators, 5 simple selectors, 7 attribute operators, 13
pseudo-classes, 4 pseudo-elements (::before, ::after, ::marker,
::placeholder).
Calls out each category's known cliffs from one place:
- Interaction-state pseudo-classes (:hover/:focus/:active/:visited/
:target/:checked/:disabled) are not supported — PDFs are static.
- :not() takes a single simple selector, not a selector list.
- Single-colon pseudo-element forms (:before, :after) are NOT
recognized — only the double-colon form routes to pseudoElement
in css.go's selector parser.
- Case-sensitivity flags ([attr="x" i]) are not parsed.
TestSelectorsDocCoverage maintains a static expected list and asserts
each name appears as a code-fenced reference in the rendered doc.
Behavioral coverage for matching is in the existing selector tests.
Closes#162.
| Class |`.note`| Matches elements whose `class` attribute contains the name. Multiple classes can be chained: `.note.warning`. |
311
+
| ID |`#title`| Matches the element with the given `id`. |
312
+
| Universal |`*`| Matches every element. |
313
+
| Attribute |`[lang]`, `[lang="en"]`| See attribute operators below. |
314
+
315
+
Selectors compose: `article.featured > p.lead` matches a `p` with class `lead` that is a direct child of an `article` with class `featured`.
316
+
317
+
### Attribute operators
318
+
319
+
| Operator | Example | Matches when... |
320
+
|---|---|---|
321
+
| presence |`[hidden]`| Attribute is present (any value, including empty). |
322
+
|`=`|`[type="submit"]`| Attribute value equals the operand exactly. |
323
+
|`^=`|`[href^="https://"]`| Value starts with the operand. |
324
+
|`$=`|`[src$=".pdf"]`| Value ends with the operand. |
325
+
|`*=`|`[class*="btn"]`| Value contains the operand as a substring. |
326
+
|`~=`|`[rel~="author"]`| Value, treated as a whitespace-separated list, contains the operand as a whole word. |
327
+
| `|=` | `[lang|="en"]` | Value equals the operand or starts with `operand-`. |
328
+
329
+
Case-sensitivity flags (`[lang="EN" i]`) are not parsed.
330
+
331
+
### Pseudo-classes
332
+
333
+
| Pseudo-class | Notes |
334
+
|---|---|
335
+
|`:root`| The document root (`<html>`). |
336
+
|`:empty`| Element with no element children and no non-empty text nodes. |
337
+
|`:first-child`| First child of its parent. |
338
+
|`:last-child`| Last child of its parent. |
339
+
|`:nth-child(<expr>)`| Position match. `<expr>` accepts `odd`, `even`, an integer, or `An+B` form (e.g. `2n+1`, `3n`, `-n+3`). |
340
+
|`:nth-last-child(<expr>)`| Same as `:nth-child` but counted from the end. |
341
+
|`:first-of-type`| First element of its tag type among siblings. |
342
+
|`:last-of-type`| Last element of its tag type among siblings. |
343
+
|`:nth-of-type(<expr>)`| Position match restricted to the element's tag type. |
344
+
|`:nth-last-of-type(<expr>)`| Same, counted from the end. |
345
+
|`:not(<simple>)`| Negation. Argument is a single simple selector — selector lists inside `:not()` are not parsed. |
346
+
|`:is(<list>)`| Matches if any selector in the comma-separated list matches. Specificity follows the highest-specificity argument. |
347
+
|`:where(<list>)`| Same matching as `:is()` but contributes zero specificity. |
348
+
349
+
Interaction-state pseudo-classes (`:hover`, `:focus`, `:active`, `:visited`, `:target`, `:checked`, `:disabled`) are not supported — PDFs are static.
350
+
351
+
### Pseudo-elements
352
+
353
+
| Pseudo-element | Notes |
354
+
|---|---|
355
+
|`::before`| Inserts generated content before the element. Driven by the `content` declaration. |
356
+
|`::after`| Inserts generated content after the element. |
357
+
|`::marker`| Styles the list marker on `<li>` elements (`color`, `font-size`, etc.). |
358
+
|`::placeholder`| Styles the placeholder text on form fields. |
359
+
360
+
The double-colon form is required — single-colon legacy forms (`:before`, `:after`) are not recognized. `::first-letter`, `::first-line`, `::selection`, `::backdrop` are not supported.
361
+
290
362
## At-rules
291
363
292
364
CSS at-rules recognized by Folio's stylesheet parser. Anything not listed here
b.WriteString("> Auto-generated from `html/css_props.go`, `html/css.go`, and the function parsers in `html/`. Do not edit by hand.\n")
26
+
b.WriteString("> Auto-generated from `html/css_props.go`, `html/css.go`, `html/css_selectors.go`, and the function parsers in `html/`. Do not edit by hand.\n")
27
27
b.WriteString("> Run `go generate ./html/...` to regenerate after changing the registry.\n\n")
28
28
b.WriteString("Folio's HTML-to-PDF converter recognizes the CSS properties listed below.\n")
29
29
b.WriteString("Properties not in this document are silently ignored at render time.\n\n")
b.WriteString("Selectors compose: `article.featured > p.lead` matches a `p` with class `lead` that is a direct child of an `article` with class `featured`.\n\n")
187
+
b.WriteString("### Attribute operators\n\n")
188
+
b.WriteString("| Operator | Example | Matches when... |\n")
189
+
b.WriteString("|---|---|---|\n")
190
+
b.WriteString("| presence | `[hidden]` | Attribute is present (any value, including empty). |\n")
191
+
b.WriteString("| `=` | `[type=\"submit\"]` | Attribute value equals the operand exactly. |\n")
192
+
b.WriteString("| `^=` | `[href^=\"https://\"]` | Value starts with the operand. |\n")
193
+
b.WriteString("| `$=` | `[src$=\".pdf\"]` | Value ends with the operand. |\n")
194
+
b.WriteString("| `*=` | `[class*=\"btn\"]` | Value contains the operand as a substring. |\n")
195
+
b.WriteString("| `~=` | `[rel~=\"author\"]` | Value, treated as a whitespace-separated list, contains the operand as a whole word. |\n")
196
+
b.WriteString("| `|=` | `[lang|=\"en\"]` | Value equals the operand or starts with `operand-`. |\n\n")
197
+
b.WriteString("Case-sensitivity flags (`[lang=\"EN\" i]`) are not parsed.\n\n")
198
+
b.WriteString("### Pseudo-classes\n\n")
199
+
b.WriteString("| Pseudo-class | Notes |\n")
200
+
b.WriteString("|---|---|\n")
201
+
b.WriteString("| `:root` | The document root (`<html>`). |\n")
202
+
b.WriteString("| `:empty` | Element with no element children and no non-empty text nodes. |\n")
203
+
b.WriteString("| `:first-child` | First child of its parent. |\n")
204
+
b.WriteString("| `:last-child` | Last child of its parent. |\n")
205
+
b.WriteString("| `:nth-child(<expr>)` | Position match. `<expr>` accepts `odd`, `even`, an integer, or `An+B` form (e.g. `2n+1`, `3n`, `-n+3`). |\n")
206
+
b.WriteString("| `:nth-last-child(<expr>)` | Same as `:nth-child` but counted from the end. |\n")
207
+
b.WriteString("| `:first-of-type` | First element of its tag type among siblings. |\n")
208
+
b.WriteString("| `:last-of-type` | Last element of its tag type among siblings. |\n")
209
+
b.WriteString("| `:nth-of-type(<expr>)` | Position match restricted to the element's tag type. |\n")
210
+
b.WriteString("| `:nth-last-of-type(<expr>)` | Same, counted from the end. |\n")
211
+
b.WriteString("| `:not(<simple>)` | Negation. Argument is a single simple selector — selector lists inside `:not()` are not parsed. |\n")
212
+
b.WriteString("| `:is(<list>)` | Matches if any selector in the comma-separated list matches. Specificity follows the highest-specificity argument. |\n")
213
+
b.WriteString("| `:where(<list>)` | Same matching as `:is()` but contributes zero specificity. |\n\n")
214
+
b.WriteString("Interaction-state pseudo-classes (`:hover`, `:focus`, `:active`, `:visited`, `:target`, `:checked`, `:disabled`) are not supported — PDFs are static.\n\n")
215
+
b.WriteString("### Pseudo-elements\n\n")
216
+
b.WriteString("| Pseudo-element | Notes |\n")
217
+
b.WriteString("|---|---|\n")
218
+
b.WriteString("| `::before` | Inserts generated content before the element. Driven by the `content` declaration. |\n")
219
+
b.WriteString("| `::after` | Inserts generated content after the element. |\n")
220
+
b.WriteString("| `::marker` | Styles the list marker on `<li>` elements (`color`, `font-size`, etc.). |\n")
221
+
b.WriteString("| `::placeholder` | Styles the placeholder text on form fields. |\n\n")
222
+
b.WriteString("The double-colon form is required — single-colon legacy forms (`:before`, `:after`) are not recognized. `::first-letter`, `::first-line`, `::selection`, `::backdrop` are not supported.\n\n")
223
+
162
224
// At-rules — hand-curated section. The set of @-rules Folio
163
225
// recognizes is small and changes rarely; a parallel registry would
164
226
// be more bookkeeping than the doc payoff justifies. The
t.Errorf("%s %q is in the documented-selectors list but not present in CSS_SUPPORT.md — add it to the Selectors section in html/css_props_doc.go", group, name)
0 commit comments