.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35)
.\"
.\" Standard preamble:
.\" ========================================================================
.de Sp \" Vertical space (when we can't use .PP)
.if t .sp .5v
.if n .sp
..
.de Vb \" Begin verbatim text
.ft CW
.nf
.ne \\$1
..
.de Ve \" End verbatim text
.ft R
.fi
..
.\" Set up some character translations and predefined strings. \*(-- will
.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
.\" double quote, and \*(R" will give a right double quote. \*(C+ will
.\" give a nicer C++. Capital omega is used to do unbreakable dashes and
.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff,
.\" nothing in troff, for use with C<>.
.tr \(*W-
.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
.ie n \{\
. ds -- \(*W-
. ds PI pi
. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
. ds L" ""
. ds R" ""
. ds C` ""
. ds C' ""
'br\}
.el\{\
. ds -- \|\(em\|
. ds PI \(*p
. ds L" ``
. ds R" ''
. ds C`
. ds C'
'br\}
.\"
.\" Escape single quotes in literal strings from groff's Unicode transform.
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.\"
.\" If the F register is >0, we'll generate index entries on stderr for
.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
.\" entries marked with X<> in POD. Of course, you'll have to process the
.\" output yourself in some meaningful fashion.
.\"
.\" Avoid warning from groff about undefined register 'F'.
.de IX
..
.nr rF 0
.if \n(.g .if rF .nr rF 1
.if (\n(rF:(\n(.g==0)) \{\
. if \nF \{\
. de IX
. tm Index:\\$1\t\\n%\t"\\$2"
..
. if !\nF==2 \{\
. nr % 0
. nr F 2
. \}
. \}
.\}
.rr rF
.\" ========================================================================
.\"
.IX Title "Mojo::DOM::CSS 3"
.TH Mojo::DOM::CSS 3 "2023-03-08" "perl v5.26.3" "User Contributed Perl Documentation"
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
.\" way too many mistakes in technical documents.
.if n .ad l
.nh
.SH "NAME"
Mojo::DOM::CSS \- CSS selector engine
.SH "SYNOPSIS"
.IX Header "SYNOPSIS"
.Vb 1
\& use Mojo::DOM::CSS;
\&
\& # Select elements from DOM tree
\& my $css = Mojo::DOM::CSS\->new(tree => $tree);
\& my $elements = $css\->select(\*(Aqh1, h2, h3\*(Aq);
.Ve
.SH "DESCRIPTION"
.IX Header "DESCRIPTION"
Mojo::DOM::CSS is the \s-1CSS\s0 selector engine used by Mojo::DOM, based on the \s-1HTML\s0 Living
Standard <https://html.spec.whatwg.org> and Selectors Level 3 <https://www.w3.org/TR/css3-selectors/>.
.SH "SELECTORS"
.IX Header "SELECTORS"
All \s-1CSS\s0 selectors that make sense for a standalone parser are supported.
.SS "*"
.IX Subsection "*"
Any element.
.PP
.Vb 1
\& my $all = $css\->select(\*(Aq*\*(Aq);
.Ve
.SS "E"
.IX Subsection "E"
An element of type \f(CW\*(C`E\*(C'\fR.
.PP
.Vb 1
\& my $title = $css\->select(\*(Aqtitle\*(Aq);
.Ve
.SS "E[foo]"
.IX Subsection "E[foo]"
An \f(CW\*(C`E\*(C'\fR element with a \f(CW\*(C`foo\*(C'\fR attribute.
.PP
.Vb 1
\& my $links = $css\->select(\*(Aqa[href]\*(Aq);
.Ve
.ie n .SS "E[foo=""bar""]"
.el .SS "E[foo=``bar'']"
.IX Subsection "E[foo=bar]"
An \f(CW\*(C`E\*(C'\fR element whose \f(CW\*(C`foo\*(C'\fR attribute value is exactly equal to \f(CW\*(C`bar\*(C'\fR.
.PP
.Vb 2
\& my $case_sensitive = $css\->select(\*(Aqinput[type="hidden"]\*(Aq);
\& my $case_sensitive = $css\->select(\*(Aqinput[type=hidden]\*(Aq);
.Ve
.ie n .SS "E[foo=""bar"" i]"
.el .SS "E[foo=``bar'' i]"
.IX Subsection "E[foo=bar i]"
An \f(CW\*(C`E\*(C'\fR element whose \f(CW\*(C`foo\*(C'\fR attribute value is exactly equal to any (ASCII-range) case-permutation of \f(CW\*(C`bar\*(C'\fR. Note
that this selector is \fB\s-1EXPERIMENTAL\s0\fR and might change without warning!
.PP
.Vb 3
\& my $case_insensitive = $css\->select(\*(Aqinput[type="hidden" i]\*(Aq);
\& my $case_insensitive = $css\->select(\*(Aqinput[type=hidden i]\*(Aq);
\& my $case_insensitive = $css\->select(\*(Aqinput[class~="foo" i]\*(Aq);
.Ve
.PP
This selector is part of Selectors Level 4 <https://dev.w3.org/csswg/selectors-4>, which is still a work in progress.
.ie n .SS "E[foo=""bar"" s]"
.el .SS "E[foo=``bar'' s]"
.IX Subsection "E[foo=bar s]"
An \f(CW\*(C`E\*(C'\fR element whose \f(CW\*(C`foo\*(C'\fR attribute value is exactly and case-sensitively equal to \f(CW\*(C`bar\*(C'\fR. Note that this selector
is \fB\s-1EXPERIMENTAL\s0\fR and might change without warning!
.PP
.Vb 1
\& my $case_sensitive = $css\->select(\*(Aqinput[type="hidden" s]\*(Aq);
.Ve
.PP
This selector is part of Selectors Level 4 <https://dev.w3.org/csswg/selectors-4>, which is still a work in progress.
.ie n .SS "E[foo~=""bar""]"
.el .SS "E[foo~=``bar'']"
.IX Subsection "E[foo~=bar]"
An \f(CW\*(C`E\*(C'\fR element whose \f(CW\*(C`foo\*(C'\fR attribute value is a list of whitespace-separated values, one of which is exactly equal to
\&\f(CW\*(C`bar\*(C'\fR.
.PP
.Vb 2
\& my $foo = $css\->select(\*(Aqinput[class~="foo"]\*(Aq);
\& my $foo = $css\->select(\*(Aqinput[class~=foo]\*(Aq);
.Ve
.ie n .SS "E[foo^=""bar""]"
.el .SS "E[foo^=``bar'']"
.IX Subsection "E[foo^=bar]"
An \f(CW\*(C`E\*(C'\fR element whose \f(CW\*(C`foo\*(C'\fR attribute value begins exactly with the string \f(CW\*(C`bar\*(C'\fR.
.PP
.Vb 2
\& my $begins_with = $css\->select(\*(Aqinput[name^="f"]\*(Aq);
\& my $begins_with = $css\->select(\*(Aqinput[name^=f]\*(Aq);
.Ve
.ie n .SS "E[foo$=""bar""]"
.el .SS "E[foo$=``bar'']"
.IX Subsection "E[foo$=bar]"
An \f(CW\*(C`E\*(C'\fR element whose \f(CW\*(C`foo\*(C'\fR attribute value ends exactly with the string \f(CW\*(C`bar\*(C'\fR.
.PP
.Vb 2
\& my $ends_with = $css\->select(\*(Aqinput[name$="o"]\*(Aq);
\& my $ends_with = $css\->select(\*(Aqinput[name$=o]\*(Aq);
.Ve
.ie n .SS "E[foo*=""bar""]"
.el .SS "E[foo*=``bar'']"
.IX Subsection "E[foo*=bar]"
An \f(CW\*(C`E\*(C'\fR element whose \f(CW\*(C`foo\*(C'\fR attribute value contains the substring \f(CW\*(C`bar\*(C'\fR.
.PP
.Vb 2
\& my $contains = $css\->select(\*(Aqinput[name*="fo"]\*(Aq);
\& my $contains = $css\->select(\*(Aqinput[name*=fo]\*(Aq);
.Ve
.ie n .SS "E[foo|=""en""]"
.el .SS "E[foo|=``en'']"
.IX Subsection "E[foo|=en]"
An \f(CW\*(C`E\*(C'\fR element whose \f(CW\*(C`foo\*(C'\fR attribute has a hyphen-separated list of values beginning (from the left) with \f(CW\*(C`en\*(C'\fR.
.PP
.Vb 1
\& my $english = $css\->select(\*(Aqlink[hreflang|=en]\*(Aq);
.Ve
.SS "E:root"
.IX Subsection "E:root"
An \f(CW\*(C`E\*(C'\fR element, root of the document.
.PP
.Vb 1
\& my $root = $css\->select(\*(Aq:root\*(Aq);
.Ve
.SS "E:nth\-child(n)"
.IX Subsection "E:nth-child(n)"
An \f(CW\*(C`E\*(C'\fR element, the \f(CW\*(C`n\-th\*(C'\fR child of its parent.
.PP
.Vb 4
\& my $third = $css\->select(\*(Aqdiv:nth\-child(3)\*(Aq);
\& my $odd = $css\->select(\*(Aqdiv:nth\-child(odd)\*(Aq);
\& my $even = $css\->select(\*(Aqdiv:nth\-child(even)\*(Aq);
\& my $top3 = $css\->select(\*(Aqdiv:nth\-child(\-n+3)\*(Aq);
.Ve
.SS "E:nth\-last\-child(n)"
.IX Subsection "E:nth-last-child(n)"
An \f(CW\*(C`E\*(C'\fR element, the \f(CW\*(C`n\-th\*(C'\fR child of its parent, counting from the last one.
.PP
.Vb 4
\& my $third = $css\->select(\*(Aqdiv:nth\-last\-child(3)\*(Aq);
\& my $odd = $css\->select(\*(Aqdiv:nth\-last\-child(odd)\*(Aq);
\& my $even = $css\->select(\*(Aqdiv:nth\-last\-child(even)\*(Aq);
\& my $bottom3 = $css\->select(\*(Aqdiv:nth\-last\-child(\-n+3)\*(Aq);
.Ve
.SS "E:nth\-of\-type(n)"
.IX Subsection "E:nth-of-type(n)"
An \f(CW\*(C`E\*(C'\fR element, the \f(CW\*(C`n\-th\*(C'\fR sibling of its type.
.PP
.Vb 4
\& my $third = $css\->select(\*(Aqdiv:nth\-of\-type(3)\*(Aq);
\& my $odd = $css\->select(\*(Aqdiv:nth\-of\-type(odd)\*(Aq);
\& my $even = $css\->select(\*(Aqdiv:nth\-of\-type(even)\*(Aq);
\& my $top3 = $css\->select(\*(Aqdiv:nth\-of\-type(\-n+3)\*(Aq);
.Ve
.SS "E:nth\-last\-of\-type(n)"
.IX Subsection "E:nth-last-of-type(n)"
An \f(CW\*(C`E\*(C'\fR element, the \f(CW\*(C`n\-th\*(C'\fR sibling of its type, counting from the last one.
.PP
.Vb 4
\& my $third = $css\->select(\*(Aqdiv:nth\-last\-of\-type(3)\*(Aq);
\& my $odd = $css\->select(\*(Aqdiv:nth\-last\-of\-type(odd)\*(Aq);
\& my $even = $css\->select(\*(Aqdiv:nth\-last\-of\-type(even)\*(Aq);
\& my $bottom3 = $css\->select(\*(Aqdiv:nth\-last\-of\-type(\-n+3)\*(Aq);
.Ve
.SS "E:first\-child"
.IX Subsection "E:first-child"
An \f(CW\*(C`E\*(C'\fR element, first child of its parent.
.PP
.Vb 1
\& my $first = $css\->select(\*(Aqdiv p:first\-child\*(Aq);
.Ve
.SS "E:last\-child"
.IX Subsection "E:last-child"
An \f(CW\*(C`E\*(C'\fR element, last child of its parent.
.PP
.Vb 1
\& my $last = $css\->select(\*(Aqdiv p:last\-child\*(Aq);
.Ve
.SS "E:first\-of\-type"
.IX Subsection "E:first-of-type"
An \f(CW\*(C`E\*(C'\fR element, first sibling of its type.
.PP
.Vb 1
\& my $first = $css\->select(\*(Aqdiv p:first\-of\-type\*(Aq);
.Ve
.SS "E:last\-of\-type"
.IX Subsection "E:last-of-type"
An \f(CW\*(C`E\*(C'\fR element, last sibling of its type.
.PP
.Vb 1
\& my $last = $css\->select(\*(Aqdiv p:last\-of\-type\*(Aq);
.Ve
.SS "E:only\-child"
.IX Subsection "E:only-child"
An \f(CW\*(C`E\*(C'\fR element, only child of its parent.
.PP
.Vb 1
\& my $lonely = $css\->select(\*(Aqdiv p:only\-child\*(Aq);
.Ve
.SS "E:only\-of\-type"
.IX Subsection "E:only-of-type"
An \f(CW\*(C`E\*(C'\fR element, only sibling of its type.
.PP
.Vb 1
\& my $lonely = $css\->select(\*(Aqdiv p:only\-of\-type\*(Aq);
.Ve
.SS "E:empty"
.IX Subsection "E:empty"
An \f(CW\*(C`E\*(C'\fR element that has no children (including text nodes).
.PP
.Vb 1
\& my $empty = $css\->select(\*(Aq:empty\*(Aq);
.Ve
.SS "E:any\-link"
.IX Subsection "E:any-link"
Alias for \*(L"E:link\*(R". Note that this selector is \fB\s-1EXPERIMENTAL\s0\fR and might change without warning! This selector is
part of Selectors Level 4 <https://dev.w3.org/csswg/selectors-4>, which is still a work in progress.
.SS "E:link"
.IX Subsection "E:link"
An \f(CW\*(C`E\*(C'\fR element being the source anchor of a hyperlink of which the target is not yet visited (\f(CW\*(C`:link\*(C'\fR) or already
visited (\f(CW\*(C`:visited\*(C'\fR). Note that Mojo::DOM::CSS is not stateful, therefore \f(CW\*(C`:any\-link\*(C'\fR, \f(CW\*(C`:link\*(C'\fR and \f(CW\*(C`:visited\*(C'\fR
yield exactly the same results.
.PP
.Vb 3
\& my $links = $css\->select(\*(Aq:any\-link\*(Aq);
\& my $links = $css\->select(\*(Aq:link\*(Aq);
\& my $links = $css\->select(\*(Aq:visited\*(Aq);
.Ve
.SS "E:visited"
.IX Subsection "E:visited"
Alias for \*(L"E:link\*(R".
.SS "E:scope"
.IX Subsection "E:scope"
An \f(CW\*(C`E\*(C'\fR element being a designated reference element. Note that this selector is \fB\s-1EXPERIMENTAL\s0\fR and might change
without warning!
.PP
.Vb 3
\& my $scoped = $css\->select(\*(Aqa:not(:scope > a)\*(Aq);
\& my $scoped = $css\->select(\*(Aqdiv :scope p\*(Aq);
\& my $scoped = $css\->select(\*(Aq~ p\*(Aq);
.Ve
.PP
This selector is part of Selectors Level 4 <https://dev.w3.org/csswg/selectors-4>, which is still a work in progress.
.SS "E:checked"
.IX Subsection "E:checked"
A user interface element \f(CW\*(C`E\*(C'\fR which is checked (for instance a radio-button or checkbox).
.PP
.Vb 1
\& my $input = $css\->select(\*(Aq:checked\*(Aq);
.Ve
.SS "E.warning"
.IX Subsection "E.warning"
An \f(CW\*(C`E\*(C'\fR element whose class is \*(L"warning\*(R".
.PP
.Vb 1
\& my $warning = $css\->select(\*(Aqdiv.warning\*(Aq);
.Ve
.SS "E#myid"
.IX Subsection "E#myid"
An \f(CW\*(C`E\*(C'\fR element with \f(CW\*(C`ID\*(C'\fR equal to \*(L"myid\*(R".
.PP
.Vb 1
\& my $foo = $css\->select(\*(Aqdiv#foo\*(Aq);
.Ve
.SS "E:not(s1, s2)"
.IX Subsection "E:not(s1, s2)"
An \f(CW\*(C`E\*(C'\fR element that does not match either compound selector \f(CW\*(C`s1\*(C'\fR or compound selector \f(CW\*(C`s2\*(C'\fR. Note that support for
compound selectors is \fB\s-1EXPERIMENTAL\s0\fR and might change without warning!
.PP
.Vb 1
\& my $others = $css\->select(\*(Aqdiv p:not(:first\-child, :last\-child)\*(Aq);
.Ve
.PP
Support for compound selectors was added as part of Selectors Level 4 <https://dev.w3.org/csswg/selectors-4>, which is
still a work in progress.
.SS "E:is(s1, s2)"
.IX Subsection "E:is(s1, s2)"
An \f(CW\*(C`E\*(C'\fR element that matches compound selector \f(CW\*(C`s1\*(C'\fR and/or compound selector \f(CW\*(C`s2\*(C'\fR. Note that this selector is
\&\fB\s-1EXPERIMENTAL\s0\fR and might change without warning!
.PP
.Vb 1
\& my $headers = $css\->select(\*(Aq:is(section, article, aside, nav) h1\*(Aq);
.Ve
.PP
This selector is part of Selectors Level 4 <https://dev.w3.org/csswg/selectors-4>, which is still a work in progress.
.SS "E:has(rs1, rs2)"
.IX Subsection "E:has(rs1, rs2)"
An \f(CW\*(C`E\*(C'\fR element, if either of the relative selectors \f(CW\*(C`rs1\*(C'\fR or \f(CW\*(C`rs2\*(C'\fR, when evaluated with \f(CW\*(C`E\*(C'\fR as the :scope elements,
match an element. Note that this selector is \fB\s-1EXPERIMENTAL\s0\fR and might change without warning!
.PP
.Vb 1
\& my $link = $css\->select(\*(Aqa:has(> img)\*(Aq);
.Ve
.PP
This selector is part of Selectors Level 4 <https://dev.w3.org/csswg/selectors-4>, which is still a work in progress.
Also be aware that this feature is currently marked \f(CW\*(C`at\-risk\*(C'\fR, so there is a high chance that it will get removed
completely.
.SS "E:text(string_or_regex)"
.IX Subsection "E:text(string_or_regex)"
An \f(CW\*(C`E\*(C'\fR element containing text content that substring matches \f(CW\*(C`string_or_regex\*(C'\fR case-insensitively or that regex
matches \f(CW\*(C`string_or_regex\*(C'\fR. For regular expressions use the format \f(CW\*(C`:text(/.../)\*(C'\fR. Note that this selector is
\&\fB\s-1EXPERIMENTAL\s0\fR and might change without warning!
.PP
.Vb 2
\& # Substring match
\& my $login = $css\->select(\*(Aq:text(Log in)\*(Aq);
\&
\& # Regex match
\& my $login = $css\->select(\*(Aq:text(/Log ?in/)\*(Aq);
\&
\& # Regex match (case\-insensitive)
\& my $login = $css\->select(\*(Aq:text(/(?i:Log ?in)/)\*(Aq);
.Ve
.PP
This is a custom selector for Mojo::DOM and not part of any spec.
.SS "A|E"
.IX Subsection "A|E"
An \f(CW\*(C`E\*(C'\fR element that belongs to the namespace alias \f(CW\*(C`A\*(C'\fR from \s-1CSS\s0 Namespaces Module Level
3 <https://www.w3.org/TR/css-namespaces-3/>. Key/value pairs passed to selector methods are used to declare namespace
aliases.
.PP
.Vb 1
\& my $elem = $css\->select(\*(Aqlq|elem\*(Aq, lq => \*(Aqhttp://example.com/q\-markup\*(Aq);
.Ve
.PP
Using an empty alias searches for an element that belongs to no namespace.
.PP
.Vb 1
\& my $div = $c\->select(\*(Aq|div\*(Aq);
.Ve
.SS "E F"
.IX Subsection "E F"
An \f(CW\*(C`F\*(C'\fR element descendant of an \f(CW\*(C`E\*(C'\fR element.
.PP
.Vb 1
\& my $headlines = $css\->select(\*(Aqdiv h1\*(Aq);
.Ve
.SS "E > F"
.IX Subsection "E > F"
An \f(CW\*(C`F\*(C'\fR element child of an \f(CW\*(C`E\*(C'\fR element.
.PP
.Vb 1
\& my $headlines = $css\->select(\*(Aqhtml > body > div > h1\*(Aq);
.Ve
.SS "E + F"
.IX Subsection "E + F"
An \f(CW\*(C`F\*(C'\fR element immediately preceded by an \f(CW\*(C`E\*(C'\fR element.
.PP
.Vb 1
\& my $second = $css\->select(\*(Aqh1 + h2\*(Aq);
.Ve
.SS "E ~ F"
.IX Subsection "E ~ F"
An \f(CW\*(C`F\*(C'\fR element preceded by an \f(CW\*(C`E\*(C'\fR element.
.PP
.Vb 1
\& my $second = $css\->select(\*(Aqh1 ~ h2\*(Aq);
.Ve
.SS "E, F, G"
.IX Subsection "E, F, G"
Elements of type \f(CW\*(C`E\*(C'\fR, \f(CW\*(C`F\*(C'\fR and \f(CW\*(C`G\*(C'\fR.
.PP
.Vb 1
\& my $headlines = $css\->select(\*(Aqh1, h2, h3\*(Aq);
.Ve
.SS "E[foo=bar][bar=baz]"
.IX Subsection "E[foo=bar][bar=baz]"
An \f(CW\*(C`E\*(C'\fR element whose attributes match all following attribute selectors.
.PP
.Vb 1
\& my $links = $css\->select(\*(Aqa[foo^=b][foo$=ar]\*(Aq);
.Ve
.SH "ATTRIBUTES"
.IX Header "ATTRIBUTES"
Mojo::DOM::CSS implements the following attributes.
.SS "tree"
.IX Subsection "tree"
.Vb 2
\& my $tree = $css\->tree;
\& $css = $css\->tree([\*(Aqroot\*(Aq]);
.Ve
.PP
Document Object Model. Note that this structure should only be used very carefully since it is very dynamic.
.SH "METHODS"
.IX Header "METHODS"
Mojo::DOM::CSS inherits all methods from Mojo::Base and implements the following new ones.
.SS "matches"
.IX Subsection "matches"
.Vb 2
\& my $bool = $css\->matches(\*(Aqhead > title\*(Aq);
\& my $bool = $css\->matches(\*(Aqsvg|line\*(Aq, svg => \*(Aqhttp://www.w3.org/2000/svg\*(Aq);
.Ve
.PP
Check if first node in \*(L"tree\*(R" matches the \s-1CSS\s0 selector. Trailing key/value pairs can be used to declare xml
namespace aliases.
.SS "select"
.IX Subsection "select"
.Vb 2
\& my $results = $css\->select(\*(Aqhead > title\*(Aq);
\& my $results = $css\->select(\*(Aqsvg|line\*(Aq, svg => \*(Aqhttp://www.w3.org/2000/svg\*(Aq);
.Ve
.PP
Run \s-1CSS\s0 selector against \*(L"tree\*(R". Trailing key/value pairs can be used to declare xml namespace aliases.
.SS "select_one"
.IX Subsection "select_one"
.Vb 3
\& my $result = $css\->select_one(\*(Aqhead > title\*(Aq);
\& my $result =
\& $css\->select_one(\*(Aqsvg|line\*(Aq, svg => \*(Aqhttp://www.w3.org/2000/svg\*(Aq);
.Ve
.PP
Run \s-1CSS\s0 selector against \*(L"tree\*(R" and stop as soon as the first node matched. Trailing key/value pairs can be used to
declare xml namespace aliases.
.SH "DEBUGGING"
.IX Header "DEBUGGING"
You can set the \f(CW\*(C`MOJO_DOM_CSS_DEBUG\*(C'\fR environment variable to get some advanced diagnostics information printed to
\&\f(CW\*(C`STDERR\*(C'\fR.
.PP
.Vb 1
\& MOJO_DOM_CSS_DEBUG=1
.Ve
.SH "SEE ALSO"
.IX Header "SEE ALSO"
Mojolicious, Mojolicious::Guides, <https://mojolicious.org>.