Selectors
A selector is a pattern that describes the scope of a rule, i.e. the set of HTML elements to which it should apply. Tecton supports several types of selectors, including element, class, ID, attribute, and pseudo-class selectors. You can combine these to form more advanced selectors or even a selector list.
Element selectors
An element selector targets all HTML elements of a particular type, such as div
, p
, a
, or img
. In its most basic form, an element selector simply consists of the target element type, e.g.
module Example.StyleSheet where
import Tecton
import Tecton.Rule as Rule
styleSheet :: CSS
styleSheet = do
a ? Rule.do -- Applies to HTML `a` (anchor) elements.
textDecorationLine := none
Universal selector
The universal selector is a special type of element selector that applies to any element. In CSS, it is denoted by the *
symbol. In Tecton, it is called universal
. Any selector that does not target another type of element must begin with the universal selector.
Note If you have written CSS before, you have almost certainly used the universal selector, perhaps without realizing: It is implicitly included in other types of selectors; for example,
.foo
is actually the same as*.foo
. For more information, see Selectors Level 3 (W3C).
The following rule uses the universal selector to remove the margin from every element in the webpage:
module Example.StyleSheet where
import Tecton
import Tecton.Rule as Rule
styleSheet :: CSS
styleSheet = do
universal ? Rule.do
margin := nil
Class selectors
A class selector matches any element whose class
attribute contains the specified class name. The &.
operator appends the target class to a selector. For example, the following selector targets all elements with the class name container:
module Example.StyleSheet where
import Tecton
import Tecton.Rule as Rule
styleSheet :: CSS
styleSheet = do
universal &. ClassName "container" ? Rule.do
width := auto
ID selectors
An ID selector targets the element whose id
attribute is exactly the specified value. The &#
operator appends the element ID to another selector. The selector in the following example matches the element with ID appBar:
module Example.StyleSheet where
import Tecton
import Tecton.Rule as Rule
styleSheet :: CSS
styleSheet = do
universal &# ElementId "appBar" ? Rule.do
height := px 64
Attribute selectors
An attribute selector targets elements based on their attributes or attribute values. It can match on the presence of any HTML attribute such as href
, src
, or alt
. A number of operators can further limit matches to specific attribute values.
Presence
To match elements based on the presence of an attribute, the attribute is used by itself without any additional operator. For example, the following selector applies to an input
element with a required
attribute:
module Example.StyleSheet where
import Color (rgb)
import Tecton
import Tecton.Rule as Rule
styleSheet :: CSS
styleSheet = do
input &@ required ? Rule.do
backgroundColor := rgb 255 153 0
Exact value
The @=
operator can be used to target an element whose attribute value exactly matches the specified value. For example, the following selector matches elements with the attribute role="button"
:
module Example.StyleSheet where
import Tecton
import Tecton.Rule as Rule
styleSheet :: CSS
styleSheet = do
universal &@ role @= "button" ? Rule.do
fontWeight := bold
Value within list
The ~=
operator can be used to find a matching value within a whitespace-delimited list of words. In the following example, the selector will match an element with an attribute class="foo bar baz"
. (This is, of course, equivalent to a class selector.)
module Example.StyleSheet where
import Tecton
import Tecton.Rule as Rule
styleSheet :: CSS
styleSheet = do
universal &@ class' ~= "bar" ? Rule.do
display := none
Code/subcode
Intended primarily for locale tags, the |=
operator matches when an attribute value
- exactly matches the specified value or
- begins with the specified value followed by a hyphen.
The selector in the following example will match an a
element whose hreflang
attribute is equal to "en"
or "en-US"
, but not "end"
:
module Example.StyleSheet where
import Color (rgb)
import Tecton
import Tecton.Rule as Rule
styleSheet :: CSS
styleSheet = do
a &@ hreflang |= "en" ? Rule.do
color := rgb 255 0 196
Prefix
You can use the ^=
operator to represent an element whose attribute value begins with the specified value. The following example targets insecure links based on their http:// prefix:
module Example.StyleSheet where
import Color (rgb)
import Tecton
import Tecton.Rule as Rule
styleSheet :: CSS
styleSheet = do
a &@ href ^= "http://" ? Rule.do
color := rgb 255 0 0
Suffix
The $=
operator matches an element with an attribute value ending in the specified value. This can be used to target a link to a PDF, for instance:
module Example.StyleSheet where
import Tecton
import Tecton.Rule as Rule
styleSheet :: CSS
styleSheet = do
a &@ href $= ".pdf" ? Rule.do
fontWeight := bold
Substring
To target an element whose attribute value simply contains a given substring, use the *=
operator. The following selector matches any image sourced from Wikimedia:
module Example.StyleSheet where
import Tecton
import Tecton.Rule as Rule
styleSheet :: CSS
styleSheet = do
img &@ src *= "wikimedia.org" ? Rule.do
display := initial
Pseudo-classes
A pseudo-class can be used to style elements based on their state or position in the DOM tree. You can append a pseudo-class to the end of a selector using the &:
operator. For instance, the focus
pseudo-class applies a style conditionally when the element has focus:
module Example.StyleSheet where
import Tecton
import Tecton.Rule as Rule
styleSheet :: CSS
styleSheet = do
universal &: focus ? Rule.do
outlineStyle := solid
Basic pseudo-classes
The most common pseudo-classes usually consist of a single keyword, such as the following:
link
targets an unvisited link, i.e. ana
element that the user has not yet clicked or visited.visited
targets a visited link, i.e. ana
element that the user has clicked or visited in the past.hover
matches an element while the user's mouse cursor hovers over it.focus
matches an element when it has keyboard focus. An element typically receives focus through user interaction, such as clicking on an input field.focusWithin
targets an element containing a descendant element that has keyboard focus.active
matches an element while it is being clicked, pressed, or activated.target
targets an element whose ID matches the current location hash. This is often useful with deep-linking in order to draw the user's attention to the requested section of the page.enabled
targets an interactive element (e.g.input
,select
, orbutton
) that is enabled.disabled
targets an interactive element (e.g.input
,select
, orbutton
) that is disabled.checked
matches a radio button, checkbox, or option element that is checked or selected.indeterminate
matches a checkbox element in an indeterminate state, i.e. neither checked nor unchecked.root
targets the highest-level element in the document, typically anhtml
element.firstChild
targets an element with no preceding siblings.lastChild
targets an element with no subsequent siblings.firstOfType
matches an element with no preceding siblings of the same type.lastOfType
matches an element with no subsequent siblings of the same type.onlyChild
matches an element with no siblings.onlyOfType
matches an element with no siblings of the same type.empty
matches an element with no children or that contains only empty text nodes.
The lang
pseudo-class
The lang
pseudo-class can be used to target an element based on the value of its lang
attribute (or the value inherited from an ancestor). The lang
pseudo-class is parameterized by a one- or two-part language tag consisting of an ISO 639-1 language code, optionally followed by a hyphen and an ISO 3166-1 alpha-2 country code. To construct the pseudo-class, apply the lang
function to the language tag string. For example:
module Example.StyleSheet where
import Tecton
import Tecton.Rule as Rule
styleSheet :: CSS
styleSheet = do
universal &: lang "en-US" ? Rule.do
display := none
The not
pseudo-class
The not
pseudo-class targets an element that does not match the specified selector. You can pass any selector to the not
function to construct the pseudo-class. For example, the following selector will match any input
element lacking a type
attribute:
module Example.StyleSheet where
import Color (rgb)
import Tecton
import Tecton.Rule as Rule
styleSheet :: CSS
styleSheet = do
input &: not (universal &@ type') ? Rule.do
backgroundColor := rgb 255 0 0
Nth pseudo-classes
A special group of pseudo-classes allows you to select an element by its position within the parent element. These use a formula of the form an+b where:
- a determines the frequency of matches. For example, 3n can be interpreted as every third element.
- b represents an offset, i.e. the index of the first matching element (one-indexed). For example, 2 will match the second element, before the pattern repeats.
These pseudo-classes are
nthChild
, which selects elements matching the an+b formula within their parent;nthLastChild
, which reverses the direction ofnthChild
, counting from the last child element within a container;nthOfType
, which is like a version ofnthChild
that ignores sibling elements of a different type when evaluating the an+b formula; andnthLastOfType
, which reverses the direction ofnthOfType
, counting from the last child element within a container.
The :nth Tester (CSS-Tricks) offers an interactive way to learn how these pseudo-classes and the an+b formula work.
Constructing these pseudo-classes in Tecton begins with the formula:
- The
even
function will select even-numbered elements, equivalent to 2n. - The
odd
function will select odd-numbered elements, equivalent to 2n+1. - The
#+
operator has two parameters a and b, used to create an an+b formula, e.g.2 #+ 1
. - The
#-
operator has two parameters a and b, used to create an an-b formula, e.g.2 #- 1
.
Apply one of the nth*
functions listed above to the result to create the pseudo class.
Here is how nthChild
, for instance, can be used to add zebra-striping to a table:
module Example.StyleSheet where
import Color (rgb)
import Tecton
import Tecton.Rule as Rule
styleSheet :: CSS
styleSheet = do
tr &: nthChild even ? Rule.do
backgroundColor := rgb 240 240 240
Pseudo-elements
Pseudo-elements are virtual elements that can be styled using CSS but exist outside of the DOM. These offer control and flexibility in design without modifying HTML structure. You can append a pseudo-element to a selector using the &::
operator. For example, the following adds a red asterisk symbol to a required form field label using the after
pseudo-element:
module Example.StyleSheet where
import Color (rgb)
import Tecton
import Tecton.Rule as Rule
styleSheet :: CSS
styleSheet = do
universal &. ClassName "required" &:: after ? Rule.do
content := " *"
color := rgb 255 0 0
Note that, once a pseudo-element has been appended to selector, the selector can no longer be modified; otherwise, the resulting CSS would be invalid.
Selector list
A common CSS pattern is a ruleset that targets elements matching any of a number of selectors. This is useful as a performance optimization as well to avoid duplication.
Throughout the library, lists are modeled using nested tuples via the /\
operator. Selector lists are no exception.
Here is an example of a ruleset the applies to buttons as well as hyperlinks (a
elements) masquerading as buttons:
module Example.StyleSheet where
import Data.Tuple.Nested ((/\))
import Tecton
import Tecton.Rule as Rule
styleSheet :: CSS
styleSheet = do
button /\ a &. ClassName "button" ? Rule.do
fontWeight := bold