Navigation, announcements and search
Connect shoppers to collections, guides and support before enhancing menus and search.
Announcements, conditions and moving text
Read one announcement record with conditions and a detail link. Make the static text readable before adding motion, and review announcements and reassurance copy against the same policy source.
Data and dependencies
- shop.custom.announcement is a metaobject reference.
- text is single-line text; destination is a URL.
Interaction logic
- Show plain text without a destination; omit an empty message.
- Allow motion to pause and respect reduced-motion preferences.
Example code · snippets/ww-announcement.liquid
{% assign announcement = shop.metafields.custom.announcement.value %}
{% if announcement.text.value != blank %}
<aside class="ww-announcement" aria-label="Store announcement">
{% if announcement.destination.value != blank %}
<a href="{{ announcement.destination.value | escape }}">{{ announcement.text.value | escape }}</a>
{% else %}
<p>{{ announcement.text.value | escape }}</p>
{% endif %}
</aside>
{% endif %}
Acceptance checks
- Keep one complete announcement in the DOM.
- Check currency, conditions, dates and the mobile first view.
The example is a static baseline. Populate it only with applicable shipping, warranty or promotion terms.
Official documentation
Search for products, accessories and guides
Make the native GET search return the right content types and working links before adding suggestions. Handle predictions, full results, no results and request failures separately.
Data and dependencies
- q comes from the shopper and is escaped when rendered.
- The example explicitly requests products, pages and articles.
Interaction logic
- Keyboard submission reaches the full result page.
- Debounce suggestions, cancel stale requests and expose loading and empty states.
Example code · snippets/ww-search.liquid
<form action="{{ routes.search_url }}" method="get" role="search">
<label for="Search-{{ section.id }}">Search products and guides</label>
<input id="Search-{{ section.id }}" type="search" name="q" value="{{ search.terms | escape }}">
<input type="hidden" name="type" value="product,page,article">
<button type="submit">Search</button>
</form>
{% if search.performed %}
<p role="status">{{ search.results_count }} results</p>
{% paginate search.results by 12 %}
<ul>
{% for result in search.results %}
<li><a href="{{ result.url | escape }}">{{ result.title | escape }}</a> <small>{{ result.object_type | escape }}</small></li>
{% endfor %}
</ul>
{{ paginate | default_pagination }}
{% endpaginate %}
{% endif %}
Acceptance checks
- Test a model, a help question and a no-result term.
- Compare the theme request with app result-type settings.
Place result rendering in the search template and use only the form elsewhere. Storefront result types are separate from external search rankings.
Official documentation
Read it, integrate it, verify it
- Choose a module and confirm the data contract.
- Connect the fragment to the current theme or framework.
- Check real variants, markets and recovery states.
Examples target Shopify Online Store 2.0 themes. Headless has separate data and version boundaries. Integrate fragments with actual fields, theme components and locale files.
Architecture references
Original educational examples with Shopify documentation and pinned upstream references. Follow the applicable license when adopting repository code.
References checked 2026-11-12