Contents

Techno is an Online Store 2.0 theme built on Shopify's Skeleton theme. It has no build step: the files in the theme are the files the browser gets. Liquid renders every page on the server, and JavaScript adds interactive behavior on top.

Warning

Editing theme files means your changes won't carry over when you update Techno. Duplicate your theme first, and prefer the options in Custom code.

Directory structure

techno/
├── assets/
│   ├── critical.css        shared styles, loaded on every page
│   └── techno.js           shared theme JavaScript, loaded deferred; components used on some pages keep theirs in {% javascript %} tags
├── blocks/                 theme blocks, shared between sections; a _ prefix marks a private block
├── config/
│   ├── settings_schema.json
│   └── settings_data.json  the three presets: Techno, Flagship and Datasheet
├── layout/
│   ├── theme.liquid        every page
│   └── password.liquid     the password page, without header or footer
├── listings/               each preset's own templates and header and footer groups, installed with that preset
│   ├── techno/templates/   index.json, the same as templates/index.json
│   ├── flagship/           templates/ (index, product) and sections/ (header and footer groups)
│   └── datasheet/          templates/ (index, product, collection, search, list-collections) and sections/
├── locales/
│   ├── en.default.json          storefront text
│   └── en.default.schema.json   theme editor labels
├── sections/               sections, _blocks.liquid, header-group.json and footer-group.json
├── snippets/               shared partials
└── templates/              JSON templates and gift_card.liquid

Blocks and the _blocks section

The Product, Featured product, Article, Rich text and Footer sections accept @theme, so they offer every public theme block, including blocks merchants generate in the theme editor. A block whose file name starts with _, such as blocks/_buy_buttons.liquid, is private: only the sections that list it by name offer it. Techno makes the blocks that read closest.product or closest.article, and the footer column blocks, private, so each appears only where it works. The six public blocks are Eyebrow, Heading, Text, Button, Collapsible row and Custom Liquid.

sections/_blocks.liquid is the section Shopify wraps a block in when a merchant generates it in a new section. It accepts @theme and @app, isn't offered under Add section, and can't be added to the Header group. Its styles follow Rich text's spacing.

A section's {% stylesheet %} only ships on pages where that section renders. The footer column classes and .tc-featured-product__title are defined in their sections and used by private blocks that only those sections list, so they're always present when used. If you make one of those blocks public, move its rules into the block or assets/critical.css.

Layout

layout/theme.liquid renders, in order:

  1. snippets/css-variables.liquid: resolves the ten color settings and every size setting into CSS custom properties on :root. See Design tokens.
  2. assets/critical.css, preloaded.
  3. assets/techno.js, deferred.
  4. snippets/standard-events.liquid: a module script that loads Shopify's standard events library and dispatches shopify:page:view once the document is ready. layout/password.liquid and templates/gift_card.liquid render it too. See JavaScript components › Standard events and actions.
  5. The script that configures Shopify.actions.updateCart and openCart with Techno's handlers, above {{ content_for_header }} so its configuration is the one that counts.
  6. A skip link, the optional scanline overlay, the Header group, <main id="MainContent">, the Footer group, the back-to-top button (snippets/back-to-top.liquid, while its setting is on), the Cart drawer section, the quick view dialog (snippets/quick-view-drawer.liquid, while Theme settings › Product cards › "Show quick view button" or "Show quick add button" is on), and snippets/card-swatches.liquid with no card id while "Show swatches" is on, which outputs nothing but makes the swatch script run on every page.
  7. A visually hidden live region, #tc-live-region, that announces cart, filter, search and variant updates to screen readers when no drawer is open. Each drawer renders its own copy through snippets/drawer-live-region.liquid — see Accessibility conventions.

CSS

WhereWhat goes there
assets/critical.cssShared primitives: page layout, type scale, panels, buttons, form controls, grids, drawers and the buy box
{% stylesheet %} in each section, block and snippetStyles that belong to that one component

Shopify collects the {% stylesheet %} tags into one stylesheet per page, holding only the CSS of the sections, blocks and snippets that page renders, so a component's styles are included once however many times it's used. Liquid isn't rendered inside these tags. HTML fetched later with the Section Rendering API brings its section's CSS in a <style data-section-stylesheet> element: code that inserts part of a response has to insert that element too, as the quick view, the cart sections and product recommendations do (see JavaScript components › Section Rendering), or the fetched markup can arrive without its styles.

Rules to follow:

  • No color values outside css-variables.liquid. Every color reads a --tc-* variable, so all four color palettes and merchant overrides keep working.
  • Don't lower the control border tokens. --tc-border-control and --tc-border-control-accent are the 3:1 non-text contrast floor across all four palettes.
  • Scale type with --tc-type-scale. Write sizes as calc(14px * var(--tc-type-scale)) so "Base text size" applies.
  • Motion must respect prefers-reduced-motion. critical.css already stops animations when it's set.

JavaScript

assets/techno.js holds the theme's shared JavaScript, with no dependencies. Components used on only some pages keep their scripts in their own {% javascript %} tags. The header section keeps the sticky header's <tc-sticky-header> and the menu's <tc-header-menu> in its own {% javascript %} tag, sections/announcement-bar.liquid keeps the countdown's <tc-countdown> in its, sections/quick-order-list.liquid keeps the quick order list's <tc-quick-order>, sections/slideshow.liquid keeps <tc-slideshow>, blocks/_buy_buttons.liquid keeps the sticky add-to-cart bar's <tc-sticky-buy>, snippets/back-to-top.liquid keeps <tc-back-to-top>, snippets/product-media.liquid keeps the gallery swipe and the full-screen viewer's <tc-lightbox> (the quick view renders it with script_only: true so the swipe runs on pages with no gallery of their own), snippets/quick-view-drawer.liquid keeps the quick view's <tc-quick-view>, and snippets/card-swatches.liquid keeps the card swatches' <tc-card-swatches>; Shopify bundles section and block scripts, and snippet scripts, into deferred files of their own, loaded after techno.js. Each file's script runs only on pages that render that file. All of them define custom elements that upgrade server-rendered HTML, so forms, links and <details> elements work before, and without, the script. See JavaScript components.

Maintainer notes

Theme Store rules don't allow minified code, so every comment in a shipped file is downloaded by shoppers. Long explanations live outside the shipped files instead:

Shipped fileLong notesAnchor left in the file
assets/critical.cssdocs/critical-css.md/* why: docs/critical-css.md#<slug> */
assets/techno.jsdocs/techno-js.md// why: docs/techno-js.md#<slug>
{% stylesheet %} in sections and snippetsA {%- comment -%} block before the tag/* see note: <selector> */
{% javascript %} in sections, blocks and snippetsA {%- comment -%} block before the tag/* see note: <name> */

The docs/ folder is in the source repository and isn't part of the theme you download from Shopify.

Local development

With Shopify CLI installed:

# Preview on a development store, with hot reload
shopify theme dev --store your-store.myshopify.com

# Lint Liquid, JSON and theme conventions
shopify theme check

# Upload as an unpublished theme
shopify theme push --unpublished

.shopifyignore keeps repository files such as README.md, docs/ and documentation/ out of CLI uploads.

Accessibility conventions

  • Every form input has a unique id and a <label for>.
  • Drawers are native <dialog> elements opened with showModal(), so focus is trapped and Escape closes them. Closing a drawer returns focus to the element that had it when the drawer opened. If the theme re-rendered that element in the meantime, for example after a variant change, focus goes to the element that now has the same id.
  • Dropdown menus, mega menu panels and filters are <details> elements that open without JavaScript. Escape and outside clicks close them. A header menu panel also closes when keyboard focus leaves it, and with Header › "Open menus on hover" on it opens when a mouse pointer rests on it.
  • Cart, filter and search changes are announced through a live region: #tc-live-region in the layout when no drawer is open, and the drawer's own region when one is. showModal() makes everything outside the open <dialog> inert, so a message written to the layout's region while a drawer is up is never announced. Every drawer carries a region, including the menu drawer, because a cart or filter response can land while it is open. Variant changes are announced through #tc-live-region only when no drawer is open. While one is, the page behind it is inert, and a sentence about it would overwrite the drawer's own message, so it is dropped. A variant picker inside the open drawer, as in the quick view, announces through that drawer's region.
  • Custom code that announces something writes to the reachable region itself, because assets/techno.js keeps its own announcer private (see JavaScript components). Find the region with document.querySelector('dialog.tc-drawer[open] [data-live-region]') || document.getElementById('tc-live-region'), which is the open drawer's region when a drawer is open and the layout's otherwise, and set its textContent to the message. Skip an empty message, which would only clear what the region holds.
  • Custom code should also follow the theme's timing. If a drawer has only just opened, or you can't tell how long it has been open, wait about 100 ms before writing: the drawer's region enters the accessibility tree as the drawer opens, and a message written at that moment isn't announced. The theme waits the same 100 ms before it announces an add to cart. Don't announce a change to the page behind an open drawer. The shopper can't reach that page, and the message would replace the drawer's own, which is why the theme drops variant messages while a drawer is open.
  • Buttons and controls use a 44 px minimum touch target (--tc-tap-min).
  • Machine identifiers such as SKUs, barcodes and URLs carry dir="ltr" so they read correctly on right-to-left pages.

See also