Reference Omeka S module

Theming and Markup

This page describes how BlockSuite blocks take their look from the theme, their public markup and CSS hooks, and the JavaScript behaviour, so a theme can restyle or extend them.

Colors come from the theme

Every block root carries the bst-block class, which defines a small set of --bst-* custom properties. Each one reads a --theme-* token when the theme provides it, and otherwise falls back to the browser's system colors (Canvas, CanvasText, LinkText), so blocks suit any light or dark theme even with no tokens at all.

BlockSuite token Reads Fallback Used for
--bst-surface --theme-surface Canvas Cards, inputs, slider buttons
--bst-surface-alt --theme-surface-alt 6% CanvasText on Canvas Tinted panels, image placeholders, secondary buttons
--bst-text --theme-text CanvasText Text
--bst-text-muted --theme-text-muted 72% CanvasText Descriptions, labels, inactive tabs
--bst-accent-text --theme-primary-text --bst-text Statistics figures, timeline dates
--bst-border --theme-border 20% CanvasText Card and list borders
--bst-border-strong --theme-border-strong 55% CanvasText Input and button borders (3:1 for form controls)
--bst-accent --theme-primary LinkText Primary buttons, active tab, hero fill, timeline markers
--bst-on-accent --theme-on-primary Canvas Text on --bst-accent
--bst-dark --theme-dark #1b1f24 Dark call to action panel
--bst-on-dark --theme-on-dark #fff Text on --bst-dark
--bst-link --theme-link LinkText Outline buttons, suggestion links
--bst-radius --theme-radius 8px Corners (buttons use --theme-radius-button first)
--bst-gap 1.5rem Space between cards
--bst-touch 2.75rem Minimum size of buttons and tabs (44px touch targets)

For theme authors

To theme every BlockSuite block at once, define the --theme-* tokens in your theme, for example on :root. Make each text/background pair meet WCAG contrast: --theme-text, --theme-text-muted, --theme-link and --theme-primary-text at 4.5:1 on --theme-surface and --theme-surface-alt, --theme-on-primary on --theme-primary, --theme-on-dark on --theme-dark, and --theme-border-strong at 3:1. The Vitrine theme computes all of these from its color settings.

Custom properties resolve where they are declared, so if your theme re-colors a section (a dark band, a colored background), declare the --theme-* tokens again on that section with the section's colors. Blocks inside then follow it automatically.

To restyle BlockSuite alone, set --bst-* tokens instead, on .bst-block or on one block type:

.bst-block { --bst-radius: 0; --bst-gap: 2rem; }
.bst-cta { --bst-dark: #20123a; --bst-on-dark: #fff; }

Assets

See Configuration for which stylesheet loads with which block. All styles are plain, unlayered CSS with low specificity (.bst-* classes), so a theme stylesheet loaded later overrides them.

Shared pieces

Class Element
.bst-block Root element of every block; defines the tokens
.bst-button Button link; modifiers --secondary, --outline, --small. Hover underlines rather than changing colors, so contrast holds in any theme
.bst-buttons Row of buttons (<p>)
.bst-sr-only Visually hidden text for screen readers
html.bst-js Added by a one-line head script when JavaScript runs; without it, tab and accordion panels show open and sliders scroll

Card Slider and Item Slider

<div class="bst-block card-slider-block" role="region" aria-roledescription="carousel" aria-label="…"
     data-autoplay="true" data-interval="4000" data-visible="3" style="--cards-visible: 3"
     data-label-position="Showing %1$s to %2$s of %3$s" data-label-pause="…" data-label-play="…">
  <h2 class="card-slider-block__heading">…</h2>
  <button class="card-slider-block__rotation bst-button bst-button--small bst-button--outline" type="button">Stop automatic slide show</button>
  <div class="card-slider-block__viewport">
    <div class="card-slider-block__track">
      <div class="card-slider-block__card" role="group" aria-roledescription="slide" aria-label="1 of 5">
        <div class="card-slider-block__inner">…</div>
      </div>
    </div>
  </div>
  <button class="card-slider-block__prev" type="button" aria-label="Previous slide">‹</button>
  <button class="card-slider-block__next" type="button" aria-label="Next slide">›</button>
  <div class="card-slider-block__live bst-sr-only" aria-live="polite" aria-atomic="true"></div>
</div>

In grid display the wrapper gets card-slider-block--grid, the cards are <li class="card-slider-block__card"> in a <ul class="card-slider-block__grid">, and there are no buttons or carousel roles. The Item Slider adds item-slider-block to the wrapper.

Class Element
.card-slider-block__track Flex row with gap: var(--bst-gap), moved with transform
.card-slider-block__card One card; (100% - gaps) / var(--cards-visible) wide
.card-slider-block__inner Card surface (--bst-surface, --bst-border, --bst-radius)
.card-slider-block__image Card Slider image, 4:3, object-fit: cover
.card-slider-block__content, __title, __desc, __link Card Slider text and button
.item-slide__thumbnail, .item-slide__placeholder Item image, or a plain placeholder panel
.item-slide__meta Item properties
.bst-item-prop, .item-prop--dcterms-date, … One property line, with a per-term modifier (.item-prop is kept for older theme CSS)
.item-slide__title The item title
.bst-item-prop__label, .bst-item-prop__value Property label and value (.prop-label, .prop-value kept too); machine dates are a <time datetime> inside the value
.item-slide__link Item button

Behaviour (sliders only):

  • The script measures the block's width and shows as many cards as fit at about 15rem each, up to data-visible, by setting --cards-visible on the wrapper. It re-measures when the block resizes.
  • Previous, next, ArrowLeft/ArrowRight (while focus is in the slider), swipes on touch screens and autoplay all move one card and wrap around at the ends.
  • Off-screen cards get the inert property.
  • The live region announces data-label-position after a visitor moves the slider; it is set to aria-live="off" while autoplay runs.
  • Autoplay pauses on hover and stops when keyboard focus enters the slider; the rotation button toggles it. It never starts when prefers-reduced-motion: reduce matches.

Tabs (Tabs (HTML) and Tabs (Blocks))

<div class="bst-block bst-tabs-block" id="bst-tabs-{blockId}">
  <h2 class="bst-tabs-block__heading">…</h2>
  <div class="bst-tabs-block__intro">…</div>                <!-- Tabs (Blocks) only -->
  <div role="tablist" class="bst-tabs-block__tablist" aria-label="…">
    <button role="tab" id="bst-tab-{blockId}-0" aria-controls="bst-panel-{blockId}-0"
            aria-selected="true" tabindex="0" class="bst-tabs-block__tab" type="button">…</button>
  </div>
  <div role="tabpanel" id="bst-panel-{blockId}-0" aria-labelledby="bst-tab-{blockId}-0"
       class="bst-tabs-block__panel bst-tabs-block__panel--active" tabindex="0">…</div>
  <div role="tabpanel" id="bst-panel-{blockId}-1" … class="bst-tabs-block__panel" hidden tabindex="0">…</div>
</div>

A Tabs (Blocks) container adds bst-tabs-block--group and is wrapped in the standard block <div> (with grid classes on grid pages). The blocks inside keep their own block wrappers.

Clicking a tab, or ArrowLeft, ArrowRight (wrapping), Home and End on a focused tab, activates it. Each .bst-tabs-block manages only its own tabs, so tab blocks nest. The default tab is set in the markup, so the page renders correctly before the script runs.

Accordion display

<div class="bst-block bst-accordion" id="…" data-allow-multiple="false">
  <h2 class="bst-tabs-block__heading bst-accordion__heading">…</h2>
  <div class="bst-accordion__item">
    <h3 class="bst-accordion__header">
      <button type="button" class="bst-accordion__trigger" id="…-button-0" aria-expanded="true" aria-controls="…-panel-0">
        <span class="bst-accordion__label">…</span><span class="bst-accordion__icon" aria-hidden="true"></span>
      </button>
    </h3>
    <div class="bst-accordion__panel" id="…-panel-0" role="region" aria-labelledby="…-button-0">
      <div class="bst-accordion__content">…</div>
    </div>
  </div>
  <!-- closed items: <div class="bst-accordion__panel" … hidden="until-found"> -->
</div>

Item headings are <h3> under a block heading and <h2> otherwise. Panels are regions only when there are six items or fewer. A Tabs (Blocks) accordion adds bst-accordion--group.

Closed panels use hidden="until-found": browsers that support it keep the panel's text findable with find-in-page and fire beforematch, which opens the section. Do not set display: none on .bst-accordion__panel[hidden="until-found"] in your theme, or find-in-page breaks; style spacing on .bst-accordion__content instead. ArrowUp, ArrowDown, Home and End move between section buttons.

Columns (Blocks)

<div class="block block-bstColumns …">
  <div class="bst-block bst-columns" id="bst-columns-{blockId}" data-gap="medium" data-align="stretch">
    <h2 class="bst-columns__heading">…</h2>
    <div class="bst-columns__intro">…</div>
    <div class="bst-columns__grid" style="--bst-column-tracks: repeat(3, minmax(0, 1fr))">
      <div class="bst-columns__column">…blocks…</div>
    </div>
  </div>
</div>

Below 48em the grid is a single column; from 48em it uses --bst-column-tracks. data-gap is small, medium or large (--bst-col-gap), data-align is stretch, start, center or end.

Each .bst-columns__column is a flex column that stacks its blocks with gap: var(--bst-gap); the blocks' own vertical margins are removed inside columns. With data-align="stretch", the last block in each column (and a BlockSuite block that is its only content) grows to the column's full height.

Hero

<section class="bst-block bst-hero bst-hero--image" data-height="standard" data-align="start" data-scrim="standard" aria-labelledby="bst-hero-{blockId}">
  <img class="bst-hero__image" src="…" alt="…" style="object-position: center center">
  <div class="bst-hero__content">
    <p class="bst-hero__eyebrow">…</p>
    <h2 class="bst-hero__heading" id="bst-hero-{blockId}">…</h2>
    <p class="bst-hero__text">…</p>
    <p class="bst-buttons bst-hero__buttons">…</p>
  </div>
</section>

data-height is compact (40vh), standard (60vh), tall (80vh) or screen (100svh), capped at 48rem except screen. With an image, ::before lays a gradient between 60% and 80% black (data-scrim="strong": 74% to 88%) over the photo; keep it at least 60% black if you restyle it, so white text stays at 4.5:1. Without an image the hero uses --bst-accent and --bst-on-accent.

Call to Action, Statistics, Timeline, Search Box

Block Root Hooks
Call to Action .bst-cta[data-background][data-align] __heading, __text, .bst-cta__buttons. data-background is none, tinted, accent or dark; on accent and dark the buttons, links and focus ring switch to the panel's text color
Statistics .bst-stats[data-style][data-align] __heading, __intro, __list (<ul>), __item, __value, __label, __description. data-style is plain or cards
Timeline .bst-timeline __heading, __intro, __list (<ol>), __event (marker in ::before), __date (with <time datetime> for years and ISO dates), __body, __title, __image, __text, __link
Search Box .bst-search __heading, __text, __form (role="search"), __label, __row, __input (type="search", name="fulltext_search"), __button, __suggestions

JavaScript

asset/js/bst.js is plain JavaScript with no dependencies, loaded with defer. It initializes every slider, tab block and accordion on the page once (each root gets data-bst-ready).

API Description
BlockSuite.init(root) Initializes blocks inside root (default document). Call it after inserting BlockSuite markup into the page.
BlockSuite.panelShown(panel) Fires the bst:panel-shown event on panel and a window resize event.
bst:panel-shown event Bubbles from a tab panel or accordion panel when it opens; event.detail.panel is the panel. Maps, charts and sliders inside hidden panels can listen for it (or for resize) to redraw at their visible size.
BlockSuite.version The script version.

Overriding templates

Copy any template from view/common/block-layout/ or view/common/blocksuite/ into the same path in your theme. The variables each template receives are listed in Block Data. If you keep the class names, role attributes and id patterns, the bundled script keeps working.

For the non-container blocks, a theme can instead declare alternative templates under block_templates in its theme.ini (Omeka S 4.1 block templates); editors then choose one under Configure layout, and the template receives the same variables.

Log in for Support