A pseudo-class (single colon, :) selects an element based on state or position rather than its type or attributes:
a:hover { } /* while the mouse is over it */
input:focus { } /* while it has keyboard focus */
input:disabled { } /* while it's disabled */
li:first-child { } /* the first child of its parent, regardless of type */
li:nth-child(2n) { } /* every even-numbered child */
button:not(.primary) { } /* any button that does NOT have the .primary class */
:nth-child() accepts an an+b formula: 2n selects even elements, 2n+1 (or odd) selects odd ones, and a plain number (:nth-child(3)) selects exactly that one position.
A pseudo-element (double colon, ::, though single-colon is still accepted for the original four for legacy reasons) targets a part of an element rather than the whole thing — something that isn't a real DOM node:
p::first-line { font-weight: bold; }
p::first-letter { font-size: 2em; }
.tooltip::before {
content: "ℹ️";
margin-right: 4px;
}
::before/::after require a content property (even content: "") to render at all, and are pure presentation — their content is not selectable text and not visible to most screen readers, so never put information there that a user actually needs to read.