Back to Blog
CSSSelectorsStylingFundamentals

CSS Selectors Guide

Master CSS selectors from basic to advanced for precise element targeting.

B
Bootspring Team
Engineering
September 5, 2018
7 min read

CSS selectors target HTML elements for styling. Here's a comprehensive guide from basic to advanced.

Basic Selectors#

1/* Universal selector - all elements */ 2* { 3 box-sizing: border-box; 4} 5 6/* Type selector - element name */ 7p { 8 color: black; 9} 10 11h1, h2, h3 { 12 font-weight: bold; 13} 14 15/* Class selector */ 16.button { 17 padding: 10px 20px; 18} 19 20/* ID selector (use sparingly) */ 21#header { 22 position: sticky; 23} 24 25/* Multiple classes */ 26.btn.primary.large { 27 /* Element with all three classes */ 28}

Attribute Selectors#

1/* Has attribute */ 2[disabled] { 3 opacity: 0.5; 4} 5 6/* Exact value */ 7[type="text"] { 8 border: 1px solid gray; 9} 10 11/* Value starts with */ 12[href^="https"] { 13 color: green; 14} 15 16/* Value ends with */ 17[href$=".pdf"] { 18 background-image: url('pdf-icon.png'); 19} 20 21/* Value contains */ 22[class*="icon"] { 23 display: inline-block; 24} 25 26/* Value in space-separated list */ 27[class~="featured"] { 28 border: 2px solid gold; 29} 30 31/* Value starts with (or value-) */ 32[lang|="en"] { 33 /* Matches en, en-US, en-GB, etc. */ 34} 35 36/* Case insensitive */ 37[type="submit" i] { 38 /* Matches Submit, SUBMIT, submit */ 39}

Combinators#

1/* Descendant (any level) */ 2article p { 3 line-height: 1.6; 4} 5 6/* Child (direct only) */ 7ul > li { 8 list-style: disc; 9} 10 11/* Adjacent sibling (immediately after) */ 12h2 + p { 13 font-size: 1.2em; 14} 15 16/* General sibling (any after) */ 17h2 ~ p { 18 color: #333; 19} 20 21/* Complex combinations */ 22nav > ul > li > a { 23 text-decoration: none; 24} 25 26article > p:first-child + p { 27 /* Second paragraph of article */ 28}

Pseudo-Classes#

1/* Link states */ 2a:link { color: blue; } 3a:visited { color: purple; } 4a:hover { color: red; } 5a:active { color: orange; } 6a:focus { outline: 2px solid blue; } 7 8/* Form states */ 9input:focus { border-color: blue; } 10input:disabled { opacity: 0.5; } 11input:enabled { background: white; } 12input:checked { accent-color: green; } 13input:required { border-left: 3px solid red; } 14input:optional { border-left: 3px solid gray; } 15input:valid { border-color: green; } 16input:invalid { border-color: red; } 17input:in-range { background: lightgreen; } 18input:out-of-range { background: lightcoral; } 19input:placeholder-shown { font-style: italic; } 20input:read-only { background: #f5f5f5; } 21input:read-write { background: white; } 22input:default { /* Default button/option */ } 23input:indeterminate { /* Checkbox neither checked nor unchecked */ } 24 25/* Interactive */ 26button:hover { background: lightblue; } 27details:open { background: lightyellow; } 28dialog:open { display: block; }

Structural Pseudo-Classes#

1/* First/last child */ 2li:first-child { font-weight: bold; } 3li:last-child { border-bottom: none; } 4p:only-child { margin: 0; } 5 6/* First/last of type */ 7p:first-of-type { font-size: 1.2em; } 8h2:last-of-type { margin-bottom: 0; } 9img:only-of-type { display: block; margin: 0 auto; } 10 11/* nth-child patterns */ 12tr:nth-child(odd) { background: #f9f9f9; } 13tr:nth-child(even) { background: white; } 14li:nth-child(3) { color: red; } /* Third item */ 15li:nth-child(n+4) { color: gray; } /* 4th and after */ 16li:nth-child(-n+3) { font-weight: bold; } /* First 3 */ 17li:nth-child(3n) { border-top: 1px solid; } /* Every 3rd */ 18li:nth-child(3n+1) { clear: left; } /* 1st, 4th, 7th... */ 19 20/* nth-last-child (from end) */ 21li:nth-last-child(1) { /* Same as :last-child */ } 22li:nth-last-child(2) { /* Second to last */ } 23li:nth-last-child(n+3) { /* All except last 2 */ } 24 25/* nth-of-type */ 26p:nth-of-type(2) { /* Second paragraph */ } 27img:nth-of-type(odd) { float: left; } 28img:nth-of-type(even) { float: right; } 29 30/* Empty elements */ 31p:empty { display: none; } 32td:empty::after { content: '-'; }

Negation and Matching#

1/* :not() - negation */ 2p:not(.special) { color: #333; } 3input:not([disabled]) { background: white; } 4li:not(:last-child) { border-bottom: 1px solid; } 5:not(p) { /* All except paragraphs */ } 6 7/* Multiple negations */ 8button:not(.primary):not(.secondary) { 9 background: gray; 10} 11 12/* :is() - matches any (forgiving) */ 13:is(h1, h2, h3, h4) { 14 font-family: serif; 15} 16 17article :is(h1, h2, h3) { 18 color: navy; 19} 20 21/* :is() reduces repetition */ 22/* Instead of: 23section h1, section h2, article h1, article h2 { } */ 24:is(section, article) :is(h1, h2) { 25 margin-top: 1em; 26} 27 28/* :where() - like :is() but zero specificity */ 29:where(h1, h2, h3) { 30 /* Easy to override */ 31 color: black; 32} 33 34/* :has() - parent selector */ 35article:has(img) { 36 padding: 20px; 37} 38 39form:has(input:invalid) { 40 border: 1px solid red; 41} 42 43/* Card with image vs without */ 44.card:has(> img) { 45 padding: 0; 46} 47 48.card:not(:has(> img)) { 49 padding: 20px; 50}

Pseudo-Elements#

1/* First letter/line */ 2p::first-letter { 3 font-size: 2em; 4 float: left; 5} 6 7p::first-line { 8 font-weight: bold; 9} 10 11/* Before/after content */ 12.required::after { 13 content: ' *'; 14 color: red; 15} 16 17blockquote::before { 18 content: '"'; 19 font-size: 3em; 20} 21 22/* Clear fix */ 23.clearfix::after { 24 content: ''; 25 display: table; 26 clear: both; 27} 28 29/* Selection */ 30::selection { 31 background: yellow; 32 color: black; 33} 34 35/* Placeholder */ 36input::placeholder { 37 color: #999; 38 font-style: italic; 39} 40 41/* List markers */ 42li::marker { 43 color: blue; 44 font-weight: bold; 45} 46 47/* File input button */ 48input[type="file"]::file-selector-button { 49 background: blue; 50 color: white; 51} 52 53/* Scrollbar (webkit) */ 54::-webkit-scrollbar { 55 width: 8px; 56} 57 58::-webkit-scrollbar-thumb { 59 background: #888; 60 border-radius: 4px; 61} 62 63/* Backdrop */ 64dialog::backdrop { 65 background: rgba(0, 0, 0, 0.5); 66}

Target and Focus#

1/* URL fragment target */ 2:target { 3 background: yellow; 4 animation: highlight 1s; 5} 6 7section:target { 8 border-left: 3px solid blue; 9} 10 11/* Focus states */ 12:focus { 13 outline: 2px solid blue; 14} 15 16:focus-visible { 17 /* Only keyboard focus, not click */ 18 outline: 2px solid blue; 19} 20 21:focus-within { 22 /* Parent contains focused element */ 23 border-color: blue; 24} 25 26/* Form with focused input */ 27form:focus-within { 28 box-shadow: 0 0 0 2px blue; 29}

State and Language#

1/* Language */ 2:lang(en) { quotes: '"' '"' "'" "'"; } 3:lang(fr) { quotes: '«' '»' '"' '"'; } 4 5/* Direction */ 6:dir(ltr) { text-align: left; } 7:dir(rtl) { text-align: right; } 8 9/* Root (html element) */ 10:root { 11 --primary-color: blue; 12 font-size: 16px; 13} 14 15/* Scope (with @scope) */ 16@scope (.card) { 17 :scope { border: 1px solid; } 18 p { margin: 0; } 19}

Specificity#

1/* Specificity: (inline, id, class, type) */ 2 3/* (0,0,0,1) - type */ 4p { } 5 6/* (0,0,1,0) - class */ 7.text { } 8 9/* (0,0,1,1) - class + type */ 10p.text { } 11 12/* (0,1,0,0) - id */ 13#main { } 14 15/* (0,1,1,1) - id + class + type */ 16div#main.container { } 17 18/* :is() uses highest specificity inside */ 19:is(#header, .nav) { 20 /* Specificity of #header (0,1,0,0) */ 21} 22 23/* :where() has zero specificity */ 24:where(#header, .nav) { 25 /* Specificity (0,0,0,0) */ 26} 27 28/* :not() uses argument specificity */ 29p:not(.highlight) { 30 /* (0,0,1,1) */ 31}

Practical Examples#

1/* Navigation highlighting */ 2nav a[href]:not([href^="#"]):not([href^="mailto"]) { 3 /* External links */ 4 color: blue; 5} 6 7/* Form validation styling */ 8form:has(:invalid) button[type="submit"] { 9 opacity: 0.5; 10 pointer-events: none; 11} 12 13/* Table with hover */ 14tr:nth-child(odd):hover:not(:first-child) { 15 background: lightblue; 16} 17 18/* Card grid */ 19.cards > article:nth-child(3n+1) { 20 clear: left; 21} 22 23/* Skip link */ 24a[href="#main"]:not(:focus) { 25 position: absolute; 26 left: -9999px; 27} 28 29/* Custom checkbox */ 30input[type="checkbox"]:checked + label::before { 31 content: '✓'; 32 color: green; 33}

Best Practices#

Selector Choice: ✓ Use classes for styling ✓ Use BEM or similar methodology ✓ Keep specificity low ✓ Prefer :where() for resets Performance: ✓ Avoid deep nesting ✓ Avoid universal with descendants ✓ Use efficient selectors ✓ ID is fastest (but avoid for styling) Maintainability: ✓ Be specific as needed ✓ Document complex selectors ✓ Use :is() to reduce repetition ✓ Keep selectors readable Avoid: ✗ Over-qualified selectors (div.class) ✗ Deep descendant chains ✗ !important battles ✗ ID selectors for styling

Conclusion#

CSS selectors range from simple type and class selectors to powerful pseudo-classes like :has() and :is(). Master attribute selectors for dynamic styling, structural pseudo-classes for patterns, and understand specificity to write maintainable CSS. Modern selectors like :has() enable parent selection, while :where() allows zero-specificity utility styles.

Share this article

Help spread the word about Bootspring