Skip to content

Bistro Theme

Bistro is a theme for restaurants and takeaways that take orders on their own site. It is built around finding food, not around the basket: the menu is the home page, its sections stay in reach as you scroll, dietary filters and a search box narrow it, and every dish shows its allergens. The order is always in view, beside the menu on a computer and as a bar at the foot of a phone.

It is built on top of Brock, which is built on Nova (Bistro → Brock → Nova → Bootstrap template fallback), so every page works as soon as a site switches to it, and Brock's type, spacing, buttons, product page, basket, mini basket, checkout and footer come through wherever Bistro doesn't override them. It needs Restaurant Ordering for anything to be ordered; without it the menu is a menu.

Site admins pick it under Admin → Shop Settings → Theme (Choosing a Theme); platform staff can also set it per site in System → Tenants → {site} → Theme (theme name Bistro).

Design principles

Principle What it means on the page
Brock underneath Inter on white with Brock's spacing scale, neutral ramp, buttons and radii. Bistro adds only what a restaurant needs.
The menu card's dotted leader Each dish runs into its price along a dotted leader. Section titles use the same sans as everything else; a site that wants a serif for them sets --rm-font-display (and --tb-font-display for booking titles) in its Design Brief custom CSS.
The brand colour is for ordering Only Add, Add to order, the order bar and Checkout use the site's brand colour (Brock's green until a Design Brief sets one).
Allergens before purchase UK food law wants allergens available before the customer buys: each row says what the dish contains, the item sheet lists them (or says none of the 14 are declared), and the site's allergen notice with the kitchen's phone number is on the menu and in every footer.
Open or closed, always A service strip under the header on every page says whether the kitchen is taking orders, until when, how long orders take, or when it opens again.

What comes from Brock and what Bistro replaces

From Brock Bistro's own
Fonts, spacing, colour and radius tokens (brock.css, with Nova's bdgr.css under it) The header: sticky ordering bar, service strip, the brand layout's site header (templates/bistro/fragment/nav.html). Brock's search-first header, announcement and promise strips, category row and drawer never appear.
Header basket and mini basket drawer, "added to your basket" pop-over (opened from Bistro's Your order link and from any add-to-basket form outside the menu) The menu (restaurantMenu), ordering status, the item sheet and its basket panel
Product page (with Choose options for a dish that has choices), listings, search results, basket, checkout (the step-by-step flow), confirmation The allergen and kitchen band at the top of the footer (fragment/footerNotice)
Footer: the site's footer content, legal lines and payment marks The site menu's template (extensions/menuGeneratorExtension/menu.html) and orderingStatus block
The head: fonts, meta, Stripe, Nova and Brock assets Bistro's stylesheet and script, added by the fragment/themeAssets hook in Brock's head

What Bistro redesigns

Area Highlights
Header A 64px sticky bar: logo and name, the site menu behind More (a disclosure that works without JavaScript), and Your order (Brock's header basket, with its item count; it opens Brock's mini basket). Under it, the service strip: open until 22:00 and last orders, closed and when it opens (today, tomorrow or a weekday), or paused and when it's back; the current preparation estimate; and a Collection / Delivery switch with a postcode check.
Menu page The restaurantMenu and orderingStatus extensions, normally through the menu page stereotype.
Baskets and checkout Brock's basket, checkout and confirmation, with each line's choices, note and allergens (see Lines with choices).
Footer Brock's dark footer, with a band above it holding the kitchen, its phone number and the allergen notice.

Other pages (content, account, search, listings) are Brock's (or Nova's where Brock has none) inside Bistro's header and Brock's footer.

The More panel is capped to the screen height (100dvh less the bar) and scrolls inside, so a long site menu never runs off the bottom. Bistro draws the site menu with its own template (templates/bistro/extensions/menuGeneratorExtension/menu.html, adapted from Ridge's): inside More each item with a drop-down is an accordion row.

Layouts: ordering and brand

The Site Setting Bistro layout (bistro-layout, under Settings → Ordering) picks how the site is laid out.

Value What it is
ordering (default) The header above: one bar, the site menu behind More, the service strip. Right for a single restaurant whose site is mostly its menu.
brand For a chain or brand whose site is also its marketing website (think a burger chain). The site splits in two sections, each with its own header.

In the brand layout BistroLayoutInterceptor puts bistroLayout, bistroOrderingSection (and, in the site section, bistroOrderUrl, the menu page) on the model, and the header only reads those.

  • Ordering section: the menu page (any page with the menu stereotype), /p/basket, /p/checkout, collection pages (except the site's landing collection, which is the home page), product pages, /buy, /checkout, order confirmation (/o/), order tracking (/order/) and search. These keep the ordering bar and the service strip (open until, Collection / Delivery), with Back to {site name} in place of More and Your order.
  • Site section: everything else (home, content pages, restaurants, blog, booking, account, errors). A regular header: the logo, the whole site menu as a row of links with drop-downs (use six or seven top-level items), Book a table (/book) and Order now (the menu page; left out if the site has no menu page) buttons, an account icon, and the order link once the basket has something in it. Below 992px a menu button opens a full-height panel that scrolls inside (the menu as an accordion) with the two buttons pinned to its foot. There is no service strip here.

The decision (BistroLayouts) is by path, so it needs no per-page setup. A site hides the name beside a wordmark logo with the Hide site name in navigation site detail (hideSiteNameInNav); both headers honour it.

The menu page

Give the home page (or a page called Menu) the menu page stereotype. It carries orderingStatus in topBanner, restaurantMenu in mainSection and the site menu in navMenu. New sites have the stereotype from their seed data; existing sites get it from the restaurant site upgrade (MenuStereotypes.ensure).

Bistro already shows the ordering status in its header, so it draws the orderingStatus block as nothing. Switch the site to another Nova theme and the block comes back without editing the page.

The menu: restaurantMenu

Any Nova-based theme can use it. Its templates live in templates/nova/extensions/restaurantMenu/, its assets in themes/nova/css|js/extensions/restaurantMenu/.

Setting Default What it does
sections empty The collections shown as menu sections, in order (comma-separated seoNames). Empty shows every collection that holds a menu item, by the collections' display order then name.
heading empty A heading above the menu.
showSearch on The search box and dietary chips. The chips only offer flags that some item has.
showBasket on The basket beside the menu on wide screens, and the order bar on phones.

Rows are the enabled menuItem products of each section, in the order they were added. Each row shows the name, a dotted leader and the price ("from" when sizes cost different amounts), a plain-text teaser, the dietary marks (V, VG, GF, DF, NF, H, K and the heat levels) and the allergens, an optional photo, and Add. While the kitchen is closed the menu stays browsable and Add says when ordering opens.

Tabs and search. The section tabs stick under the header and follow the scroll. Search matches names and descriptions; chips narrow to items with every chosen flag; empty sections and their tabs hide, and a live region says how many dishes match.

The item sheet. Add opens a sheet (a bottom sheet on phones, a dialog on wider screens) with the dish's photo and description, its allergens, sizes, each modifier group as radios (exactly one), checkboxes (each option once) or - / + steppers (an option more than once), a note for the kitchen and the quantity. Required groups are marked and checked, a full group takes no more, sold-out options are greyed and can't be chosen, and the button shows the running total. Adding posts in the background; the sheet closes, the basket panel refreshes, and a confirmation is announced.

Without JavaScript Add is a link to the item page (?item=<seoName> on the menu page), a plain form that posts to the basket. Sizes post as vSkuId; each choice posts as modifiers[n] with the value groupId:optionId (a stepper option is a select of groupId:optionId:count), which the basket reads as its modifiers list. The sheet loads the same page and lifts its form out, so the server is the only renderer.

From a product page. A dish with modifier groups reached outside the menu (from search, say) can't be added with a plain Add button, since its required choices need answers. Its product page (ProductController, model menuItemUrl from MenuItemLinks) shows Choose options instead, a link to the dish's item page on the first enabled page with the menu stereotype (/p/<menu>?item=<seoName>). A size picked on the product page goes along as &size=<variant skuId>, and the item form starts on it. Nova's product page, Brock's (and so Bistro's) and every theme that falls back to Nova (Ridge) show the link; Depot, Pop, Spec and Atelier have their own product pages and keep Add, which the basket refuses with a message saying a choice is needed. Dishes without choices keep the Add button everywhere.

The basket panel shows each line with its choices, note and allergens, - / + and Remove (posting to /basket/update and /basket/remove, in the background with JavaScript), the subtotal and Checkout. It is refreshed from ?rmView=basket.

Accessibility. Tabs are links with aria-current, and moving to a section focuses its heading. The sheet is a modal <dialog>: the page behind is inert, Escape or the close button closes it, and focus returns to the Add button. Steppers are real buttons with labels ("One more Extra halloumi"). Motion is reduced when the visitor asks for it.

Re-skin the menu in another theme by setting the --rm-* tokens on :root (see the top of restaurant-menu.css): --rm-bg, --rm-surface, --rm-ink, --rm-muted, --rm-line, --rm-accent, --rm-accent-ink, --rm-radius, --rm-font-display, --rm-sticky-top (height of a sticky header) and --rm-leader (dotted, or none). Brock, Depot, Pop, Atelier and Spec point --rm-sticky-top at their measured header height (--b-header-offset and the like), Ridge sets its header's 73px (63px on phones), and on Nova with the fixed header option the menu's script measures the header itself.

Ordering status: orderingStatus

A banner saying whether the kitchen is taking orders, how long orders take, and the Collection / Delivery switch. It renders nothing unless restaurant ordering is on.

Setting Default What it does
showWhenOpen on Off shows the banner only while closed or paused.
showModeSwitch on The collection / delivery switch and postcode check.

The postcode check asks GET /api/public/v1/local-delivery/check?postcode=&countryCode=GB (see Restaurant Ordering) and shows its message under the switch, without moving the switch or the opening hours beside it. A part postcode ("LS6") is sent as it is, and on the menu page the basket's subtotal goes along (&subtotal=), so a basket under the minimum hears "Add £4.50 more for delivery". Without an answer it says the postcode is checked at checkout. The choice and postcode are remembered in the browser (localStorage keys bdgr.orderMode, collect or delivery, and bdgr.orderPostcode), never sent to the server on their own. Unified checkout, on the same origin, reads them once for a food order that has no delivery method yet: Collection preselects the kitchen's collection option (when there is exactly one); Delivery fills the postcode into an empty delivery address, which brings back the local delivery options, and preselects the first. The step-by-step checkout (deliveryChooser, Nova and Brock) preselects the matching option the same way (its address step comes first, so it doesn't fill the postcode). The customer can change either, and the server checks the address and method as usual.

Every page of a shop taking restaurant orders carries orderingStatus (an OrderingStatusView, put on the model by OrderingStatusInterceptor) and orderingDeliveryCheck, so a theme header can show the status.

Lines with choices, in every Nova theme

fragments/lineItemAttributes :: customisation(lineItem) draws a line's choices ("Garlic mayo, Extra halloumi ×2 (+£3.00)"), the customer's note and the allergens as ordered, compactly and escaped. Nova's basket, checkout summary and order confirmation, Brock's basket, summary and confirmation, and the mini baskets of Brock, Depot, Pop, Spec and Atelier all include it. It draws nothing for an ordinary line.

Implementation

Piece Where
Theme record systemUpdates/2026-10-10-add-bistro-theme.json (InsertIfAbsent) and initialisation/templates.json; sort order 80 in ThemeDefaults. Bistro's base is brock and its second base nova (ThemeContext.secondBaseThemeTemplatePath); sites that had it on Nova are moved by systemUpdates/2026-10-16-rebase-bistro-on-brock.json (RebaseTheme), which changes the theme record and the copy embedded in each site.
Templates templates/bistro/fragment/{nav,themeAssets,footerNotice}.html, templates/bistro/extensions/orderingStatus/main.html, templates/bistro/extensions/menuGeneratorExtension/menu.html. There is no Bistro head or footer: Brock's, through its two hooks. See Theme Resolution.
Layout BistroLayoutInterceptor, BistroLayouts (which paths are ordering), bistro-layout in ConfigKeyDefaults
Assets themes/bistro/css/bistro.css, themes/bistro/js/bistro.js
Menu and status RestaurantMenuExtension, OrderingStatusExtension, MenuViews, OrderingStatusViews, OrderingStatusInterceptor, MenuStereotypes
Tests ThemeChainResolutionTest (Bistro → Brock → Nova → Bootstrap), BistroLayoutsTest, BistroLayoutInterceptorTest, BistroHeaderRenderTest (both layouts and sections), RestaurantMenuRenderTest (menu, item form, status and line choices across the Nova themes), MenuStereotypesTest