Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

Yes, you can style a page without any class or ID attributes, as long as the HTML already says what each element is. CSS can select elements by type (article, h2), by their position in the document (article > h2), by attributes the markup already carries (input[type="email"]), and by state such as :focus-visible or :checked. This works well for pages with a clear semantic structure. It becomes fragile when two visually different components use the same elements in the same nesting, because the stylesheet then depends on the shape of the markup rather than on a named hook.

Avoiding classes and IDs is a style choice and a constraint your team sets for itself. CSS does not require it. The rest of this guide shows which selectors work without hooks, where they stop being maintainable, and how to decide when an explicit class is the better tool.

What a selector does, and what it doesn’t need

A CSS selector is the pattern that decides which elements a rule’s declarations apply to. Type selectors, attribute selectors, combinators, and pseudo-classes are all standard selector forms defined in the CSS selector specifications, and none of them requires an author-supplied class or id. Class and ID selectors are two options among many. Removing them narrows what you can match, so the work moves from naming things to describing them through the document itself.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Start with type selectors and semantic elements

Type selectors match every element of a given name. They are the right tool for site-wide defaults and for elements whose meaning is already fixed by HTML: header, nav, main, article, section, footer, the heading levels, p, ul, a, button, and form controls.

body {
  font-family: system-ui, sans-serif;
  line-height: 1.5;
}

h1, h2, h3 {
  line-height: 1.2;
}

a {
  color: #1a5fb4;
}

A type selector applies to every instance of that element on the page. That is exactly what you want for defaults, and exactly what you do not want for a card that should look different from the next card. Keep component-specific styling for the sections below.

Narrow matches with combinators

Combinators describe how elements relate to each other in the tree. Four are relevant here:

  • Descendant (a space): nav a matches any link inside a nav, however deep.
  • Child (>): article > h2 matches only h2 elements that are direct children of an article, so a heading inside a nested aside is left alone.
  • Adjacent sibling (+): h2 + p matches a paragraph that immediately follows an h2.
  • General sibling (~): h2 ~ p matches every paragraph that follows an h2 at the same level.

Use a combinator only when the relationship means something in the document. header > nav reads as a real statement about the page. div > div > span describes layout accidents, and it will break the first time someone adds a wrapper.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use attributes that already exist

Attribute selectors match on the presence or value of an attribute. They are reliable when the attribute is there for a functional or semantic reason, not added for styling.

input[type="email"] {
  border: 1px solid #888;
}

a[href$=".pdf"]::after {
  content: " (PDF)";
  font-size: 0.85em;
}

The first rule targets a control that already declares its purpose through type. The second works only if your links consistently end in .pdf; if a URL carries a query string or a different extension, the rule silently stops matching. Treat attribute selectors as a convenience with a known edge, not as a guarantee.

Target state with pseudo-classes

Pseudo-classes select elements in a particular state, so the styling follows what the user is doing rather than a name you invented. Common ones for this approach include:

  • :focus-visible for keyboard focus indicators, which are shown when the browser decides focus should be visible.
  • :hover for pointer hover. It is not available on every device, so do not rely on it alone to signal interactivity.
  • :checked and :disabled for form controls whose state is part of the element.
button:hover,
a:hover {
  text-decoration: underline;
}

a:focus-visible,
button:focus-visible {
  outline: 2px solid #1a5fb4;
  outline-offset: 2px;
}

input:checked + label {
  font-weight: 700;
}

The last rule uses a sibling combinator with a state. It works only if the markup places the label immediately after the input, so it has the same structural dependency discussed below.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Group shared declarations with selector lists

A comma-separated selector list lets several elements share one declaration without a shared hook. The list above that covers h1, h2, h3 is an example. Lists are easy to read and keep the number of rules low, but each entry still matches by type, so a list cannot single out one component from another.

How specificity changes without IDs

Specificity decides which rule wins when several match the same element. Avoiding ID selectors removes the highest tier, which is one reason Google’s HTML/CSS Style Guide says to avoid them for styling. MDN’s guidance on conflicts also notes that ID selectors outrank class selectors.

Selector form Example Specificity (ID, class, type)
Type p, article 0,0,1
Attribute input[type="email"] 0,1,0
Pseudo-class :hover, :focus-visible 0,1,0
Class (for comparison) .card 0,1,0
ID (for comparison) #main-nav 1,0,0

Without IDs, the common override problem is usually a descendant chain that grows as a stylesheet grows. Keep chains short. A rule such as main section article p is hard to override because it is both long and tied to four levels of nesting.

Where structure-only CSS breaks

The approach has clear limits, and they appear in predictable places:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • Repeated components with different looks. Three cards built from identical article and h2 elements cannot be styled differently unless a structural difference exists. Position-based selectors such as main section:nth-child(3) fail as soon as a section is inserted before them.
  • Reordered or re-nested markup. A layout change that moves a heading into a new wrapper can silently remove a rule from every element it used to match.
  • Markup you do not fully control. CMS output, embedded widgets, and generated HTML often use generic wrappers, so element and attribute matches are less predictable than in hand-written pages.
  • Visual variants with no semantic difference. A “warning” box and a “tip” box can both be aside elements. No HTML element distinguishes them, so one of them needs an explicit hook.

When one of these cases appears, an explicit class is usually the clearest answer. It names the variant directly and survives reordering, which a position-based selector does not.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Structure-only selectors compared with explicit classes

Question Structure-only selectors Explicit classes
Markup meaning Follow HTML semantics. Good when elements already carry the meaning. The author names the distinction. Useful when HTML elements do not express it.
Reuse across pages Strong for site-wide element defaults; weak for repeated component variants. Strong for components that appear on many pages.
Resilience to edits Breaks when nesting or element order changes. Survives reordering and re-nesting, but breaks if the class name is removed or renamed.
Cascade control Low, predictable specificity; overrides are harder when chains grow long. Single-class specificity; easy to override with other classes.
Performance Not established by the sources consulted. Not established by the sources consulted.

IDs still have a non-styling role

Dropping IDs from your stylesheet does not mean dropping the id attribute entirely. Anchors and fragment links depend on it. A heading such as <h2 id="installation"> can be the target of <a href="#installation">, and the browser uses it to scroll. Keep the attribute for that purpose and do not write a rule against it. Google’s guide also notes that IDs are expected to be unique across a page, which is hard to guarantee when many people edit components.

A workable process

  1. Review the markup first. Replace generic div and span wrappers with semantic elements only where the content genuinely fits a semantic element.
  2. Write element defaults for body, headings, paragraphs, links, and lists.
  3. Add combinators only where the parent-child or sibling relationship is part of the design.
  4. Use attributes and states the markup already provides, such as type, :focus-visible, and :checked.
  5. Open the browser’s developer tools, select an element, and check the Styles panel to confirm which rules match. Rules that match more than intended usually come from a type selector that is broader than expected.
  6. When a component needs a distinct look that its markup does not express, add a single explicit class to that component and keep everything else structure-based.

Sources

  • MDN Web Docs, “Basic CSS selectors”: selector definitions and type-selector fundamentals; IDs are unique per document.
  • Google, “HTML/CSS Style Guide”: recommends avoiding ID selectors (“Avoid ID selectors.”) and unnecessary type-and-class combinations.
  • W3C, “Selectors Level 3”: attribute selector matching and ID selector semantics.
  • MDN Web Docs, “Handling conflicts”: specificity and the cascade, including the higher specificity of IDs and caution about !important.
  • web.dev, “Selectors”: additional reference on IDs and attribute selectors.

None of these sources, as consulted, gives a measured figure for selector performance or maintainability, so this article makes no numerical claim about either.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.