Skip to main content
drupalreleases
Release: Cms 2.2.3 — Update released for Drupal core (2.2.3)! Release: Easy Breadcrumb 2.0.11 — Minor update available for module easy_breadcrumb (2.0.11). Release: Bootstrap 8.x-3.42 — Minor update available for theme bootstrap (8.x-3.42). Release: Editoria11y Accessibility Checker 3.0.10 — Minor update available for module editoria11y (3.0.10). Release: Editoria11y Accessibility Checker 2.2.24 — Minor update available for module editoria11y (2.2.24). Release: Leaflet 10.4.13 — Minor update available for module leaflet (10.4.13). Release: Flag 5.1.1 — Minor update available for module flag (5.1.1). Release: MCP Sentinel 2.33.0 — Minor update available for module mcp_sentinel (2.33.0). Module Revived: Bootstrap 8.x-3.41 — Theme bootstrap updated after 6 months of inactivity (8.x-3.41). Security Coverage: Component Library — Module component_library now has official Drupal security advisory coverage.

This module provides a consistent way to identify pages using taxonomy terms, allowing themes and JavaScript to target pages by function rather than URL or content type. It automatically applies a data attribute to the body tag for each page, which can be populated by its taxonomy term, a fallback value for the content type, or a system default. This makes it easier to manage and style pages across your site, even if URLs or content types change.

If you build or style Drupal sites you know the drill: the theme fills up with selectors like .page-node-type-product .has-side-image, the JavaScript checks location.pathname === '/contact', and the tag manager fires on URL patterns that break when someone renames a path. Page Type Markers gives you one stable key per page instead: a data-page-type attribute on the <body> that says what the page is, whatever its URL or content type.

Install it and every node, the 404 and 403 pages, the login, registration and password screens and the user profile carry that attribute straight away. Editors pick the page type from a vocabulary; you target it from CSS, JavaScript or a tag-manager trigger. The module is complete on its own: no account, no key, no external service, and it is not connected to the Central Connect AI suite.

What you get

  • One attribute on every page. The <body> carries data-page-type="system.site:page.404" or whatever key applies, ready for body[data-page-type="…"] selectors and document.body.dataset.pageType.
  • A catalogue you manage as taxonomy. The Page Type Markers vocabulary (page_type_markers) comes seeded with 33 terms such as system.site:page.home or system.site:page.product, named in English, so rename, add or delete terms freely.
  • A field on every content type. field_page_type_marker is attached to each existing content type at install and to every new one automatically.
  • A vertical tab editors understand. On the node form the field sits in the sidebar as a select list of the vocabulary's terms, using Drupal's own widget; a user without edit access to the field does not get one.
  • Defaults per content type, in two places that agree. Set them on the content type's edit form or on the settings page; both write the same field default.
  • Six system routes mapped out of the box. 404, 403, login, registration, password reset and the user profile get a key with no configuration; override any of them from the settings form.
  • Keys that survive renames. The term name is the key by default; fill field_marker_key when the label editors see and the key your code reads must differ.
  • A switch, caches and an API. One checkbox turns the attribute off; pages carry the config:page_type_markers.settings cache tag and, on nodes, the bundle's field configuration tag; the route map is PageTypeMarkerResolver::ROUTE_DEFAULTS and page_type_markers.resolver can be decorated through PageTypeMarkerResolverInterface, and resolve() takes an optional CacheableMetadata that it fills in, the way core's token service does.

How it works

When a page renders, the resolver tries three sources and stops at the first hit: the term referenced by the node's field_page_type_marker, then the default of its content type, then the route map — your overrides merged over the six built-in routes. Any other page (views, taxonomy pages, a front page that is not a node) gets no attribute. The key is written during hook_preprocess_html, so it works with any theme and needs no JavaScript.

Administration lives under Administration » Configuration » System » Page Type Markers (/admin/config/system/page-type-markers): the on/off switch, one default per content type and one entry per system route. The vocabulary is edited under Structure » Taxonomy like any other.

Use cases

  • Style all product pages, or only the legal ones, with a selector that keeps working after a content type refactor.
  • Fire a tag-manager trigger on system.site:page.thank-you instead of on a URL marketing may change next month.
  • Hide a chat widget on the login and registration screens from a few lines of JavaScript.

Getting started

  1. Install the module: the vocabulary, its seed terms, the node field and the term fields are created for you.
  2. Grant administer page type markers to whoever manages defaults (administer site configuration also opens the page).
  3. Open Administration » Configuration » System » Page Type Markers (/admin/config/system/page-type-markers) and pick a default term per content type from the list.
  4. Review the terms at Structure » Taxonomy » Page Type Markers (/admin/structure/taxonomy/manage/page_type_markers/overview) and add the ones you miss.
  5. Edit a node, open the Page type marker tab, pick a term, save and inspect the <body>.

Requirements

  • Drupal 11.1 or newer, and the PHP version it requires (8.3).
  • Core modules Taxonomy, Node, Field and Options. Interface translations come from localize.drupal.org like any other contributed project; none ship with the module.
  • No contributed modules, no external service. composer require drupal/page_type_markers

Privacy and security

  • The module never opens a network connection; the attribute is read only by your own CSS, JavaScript or tag manager.
  • Exported configuration holds the on/off switch, the route overrides and the field definitions; the terms are content and the per-node choice lives on the node. No personal data is collected and no database tables are created.
  • The settings page is guarded by its own permission, and every form goes through the Form API with its CSRF protection.
  • Uninstalling removes the terms, fields and vocabulary the module created; a vocabulary that already existed under the same name is adopted and left in place.

Similar projects and how this one differs

  • Body Class lets editors type free-form CSS classes per node; this module offers one key from a shared vocabulary, with defaults per content type.
  • Node Class adds classes to the node wrapper rather than the <body>, with no catalogue or system-route mapping.
  • Body node ID class adds the node ID and type as body classes automatically, with no editorial choice.
  • Term body class turns taxonomy field values into body classes on entity pages; here the output is one data attribute that also covers pages without an entity, such as the 404 or the login.
  • Entity Class Formatter is a field formatter deriving classes from a field value per view mode; this module is a page-level resolver with a fixed attribute and a bundle default.
  • dataLayer exposes entity metadata as a JavaScript object for tag managers; Page Type Markers writes one attribute into the markup, usable from CSS as well.

Semantic page type was an earlier attempt by the same maintainer; it is obsolete and superseded by this project.

Development

Parts of this module were written with the assistance of AI-based coding tools, as required by the policy on the use of AI when contributing to Drupal. All the code has been read, corrected, tested and is maintained by the human maintainer listed below, who takes full responsibility for it.

Maintainers and sponsor

Maintained by pedroromán and sponsored by Tangram Consulting. Issues, ideas and contributions are welcome in the issue queue.

Depends on

Dependencies of the latest stable release

No dependencies recorded for this project.

Required by

Tracked projects that depend on this one

No tracked projects depend on this one yet.

Activity

Tracked releases
1
Tracked since
May 2026
Latest release
4 months ago
Releases (12 mo)
1 ▲ from 0
Maintenance
Active

Releases

Version Type Core Release date
1.0.x-dev Dev 10–11 May 8, 2026