Appearance
Customer account
The customer-account surface covers login, registration, the account dashboard, order history, order detail, and address management. This guide explains how to configure and customise each area.
For support troubleshooting, see Customer account support.
Shopify accounts mode
Shopify exposes three Customer-Accounts modes in Shopify admin → Settings → Customer accounts. The Uisce theme supports the classic customer surface fully — the seven classic templates, the four Custom Elements, and every setting in this guide apply when classic is reachable.
| Mode | Setting label in Shopify admin | What the theme renders |
|---|---|---|
| Classic only | Classic customer accounts | All /account/* routes render the theme's classic templates (login / register / account / order / addresses / activate / reset_password). Full feature set active. |
| Classic + new (mixed) | Classic + new customer accounts | /account/* continues to render classic templates. account.{shop}.com renders the Shopify-hosted new UI. The <shopify-account> Web Component in the header opens the correct overlay per Shopify's dispatch. |
| New only | New customer accounts only | /account/* 302-redirects to account.{shop}.com server-side. Classic templates become dead code automatically. Theme renders nothing on these routes — Shopify owns the surface. |
Action for merchants: Decide your customer-accounts policy in Shopify admin before publishing your theme. The theme adapts automatically — no theme code changes required when flipping modes.
Note: Theme Store reviewers test the classic surface unless your shop is configured to "New only". Toggling a dev store between modes is a Shopify Admin UI operation.
Sidebar navigation menu
The account-dashboard sidebar consumes a Shopify Online Store linklist with handle customer-account-main-menu. The theme provides a robust render path:
- If
linklists['customer-account-main-menu'].links.size > 0: the sidebar renders one<a>per merchant-configured link. - Otherwise (theme-default fallback): the sidebar renders 5 hard-coded links — Profile / Orders / Addresses / Privacy & data / Sign out — using the merchant's published
shop.privacy_policy.url(or/policies/privacy-policyfallback) for the privacy link.
Reviewer-resilient by design: the sidebar works whether or not the merchant provisioned the menu.
Provision the menu via Shopify admin
- Shopify admin → Online Store → Navigation → Add menu.
- Title:
Customer account main menu. Handle:customer-account-main-menu(Shopify auto-fills this from the title; verify it matches before saving). - Add 5 menu items — each as a typed link or HTTP link (the theme accepts any
MenuItemType, including HTTP URLs):Profile→/account#profileOrders→/account#ordersAddresses→/account/addressesPrivacy & data→/policies/privacy-policy(theme also resolvesshop.privacy_policy.urlautomatically — see Privacy & data link below).Sign out→/account/logout
- Save.
You can extend the menu beyond these 5 items (for example, add a custom "My subscriptions" link routing to your subscription-management portal). The theme renders every link configured.
Niche-quicklink slot
Each preset ships a niche-shaped customer-account dashboard via per-preset templates. The per-preset niche-quicklink slot lives in the sidebar, between the standard navigation links (Profile / Orders / Addresses / Privacy & data / Sign out) and the bottom of the sidebar.
| Preset | Niche | Default label | Default URL |
|---|---|---|---|
| Uisce | Services | My service area | /account#service-area |
| Gaoth | Pets | My pets | /account#pets |
| Talamh | Garden | My zones | /account#zones |
| Tine | Hardware | My specs | /account#specs |
| Neart | Wellness | My dosage | /account#dosage |
The niche-quicklink URL and label can be overridden per preset in Theme editor → Templates → Customers > Account.{preset} → Customer account section settings.
Per-preset order-detail block
Each per-preset order template includes a niche-specific block on the order-detail page. Each block consumes per-preset metafield namespaces:
| Preset | Niche | Theme block type | Metafield namespace consumed |
|---|---|---|---|
| Uisce | Services | cart-service-location | uisce_uisce.delivery_area |
| Gaoth | Pets | cart-pet-type-indicator | uisce_gaoth.pet_type |
| Talamh | Garden | cart-seasonal-availability-hint | uisce_talamh.season |
| Tine | Hardware | cart-spec-summary | uisce_tine.{wattage,size,power_source} |
| Neart | Wellness | cart-dietary-tag-stack | uisce_neart.dietary_tag |
Each block type is already authored in the cart section — the order-detail surface reuses the same theme-block contract. No new metafield namespaces are introduced.
You can add or remove the niche line-item block per preset via the theme editor → Templates → Customers > Order.{preset} → Customer order section settings → click block → Add or remove.
Privacy & data link
The sidebar nav surfaces a Privacy & data link with a shield icon. The link resolves at render time:
- Primary:
shop.privacy_policy.url— your published privacy policy URL when set in Shopify admin → Settings → Policies. - Fallback:
/policies/privacy-policy— Shopify's auto-resolved policy route. Returns 404 if no policy is published.
The link renders with rel='nofollow' (search-engine-crawl-avoidance for the theme-managed entry point; the policy itself is still indexable from the footer + checkout).
Why a published privacy policy is required
Shopify enforces published privacy + refund + terms-of-service policies for shops that target EU/UK shoppers — the policies appear automatically on the checkout page footer and the customer-rights surface. Without a published privacy policy:
- EU/UK GDPR Articles 12-22 require a clear notice of data-processing terms and the contact path for data-subject requests (access / rectification / erasure).
- Shopify's
/policies/privacy-policyroute returns 404, the sidebar link 404s in the theme-default fallback branch, and the link incustomer-account-main-menu(if you used the typedSHOP_POLICYresource type) errors at render time. - Theme Store reviewers may flag the missing policy as a compliance gap — they always test the customer-account surface and will follow the Privacy & data link.
How to publish a privacy policy
- Shopify admin → Settings → Policies → Privacy policy.
- Choose the template that fits your jurisdiction (Shopify offers UK / EU / US starters; you may need legal counsel for region-specific requirements).
- Customize the data-request mechanism (where shoppers email or click to request access / rectification / erasure).
- Save.
After saving, shop.privacy_policy.url resolves to your published policy on every storefront request — the theme picks it up automatically; no theme code change required.
Form validation
The <customer-form> Custom Element adds live JS form validation to all customer forms (login, register, addresses, activate, reset-password). On blur of any input inside the form, validators run:
- Required field check — presence validation on
requiredattributes - Email format — email-format regex on
type='email'fields - Minimum length — mirrors server-side minimum (5 characters on password fields)
- Confirm-password match — validates that the confirm-password field matches the password field
On failure: <input aria-invalid='true'> with a sibling <p role='alert'> error message and dynamic aria-describedby wiring. The summary <div role='alert'> above the form continues to render as a fallback. On submit failure: the first errored field receives focus within requestAnimationFrame and scrolls into view.
No-JS fallback: forms submit normally and Shopify's server-side errors still render.
Country and province selector
The <country-province-selector> Custom Element powers the province dropdown on the address form. When a shopper changes the selected country:
- The province
<select>populates immediately with the provinces for that country - The province select hides when the selected country has no provinces (for example, Belgium or Singapore)
- Labels update to locale-aware versions:
ZIP codefor US,Postcodefor UK,PIN codefor India,Postal codefor Canada
This replaces an older approach that depended on Shopify.CountryProvinceSelector from shopify_common.js — a global that the Uisce theme does not load. The Custom Element is self-contained and carries no external script dependency.
Order detail features
Visual order timeline
The <order-timeline> component derives order state from order.financial_status and order.fulfillment_status:
- Placed — always present; date =
order.created_at - Confirmed — when
order.financial_statusispaidorpartially_paid - Fulfilled — when
order.fulfillment_statusisfulfilledorpartial
Past states render with a green check icon. The current state renders with aria-current='step' and a pulsing accent border (gated by prefers-reduced-motion: no-preference). Future states render with muted color.
Cancelled orders collapse the timeline to a single Cancelled card showing order.cancelled_at and order.cancel_reason_label.
The timeline shows Placed, Confirmed, and Fulfilled steps. Per-shipment tracking events are not available from the Shopify Storefront API. A styled tracking-link button renders when tracking information is available.
Re-order CTA
The <order-reorder> Custom Element renders a Re-order button near the top of the order-detail page. On click:
- The button disables and shows a spinner.
- Each line item is added to the cart individually via
window.CartAPI.add(). - Line items that are no longer available are skipped.
- A toast message summarises the result: all added successfully, some skipped, or none available.
- The cart drawer opens once after all lines are processed.
The re-order CTA uses the same CartAPI.add() interface as the rest of the theme.
Print receipt
A Print receipt button triggers window.print(). The @media print stylesheet hides the header, footer, sidebar, cart drawer, and toast, and boxes the order summary cleanly on a single A4 sheet. Links render with their href visible for paper-trail traceability.
Refund panel
When order.refunds.size > 0, the refund panel renders below the line-items table listing each refund: amount, date, line items refunded, and restock note.
Subscription card
When item.selling_plan_allocation is present, the subscription card renders next to the line item with the cadence label and a manage-subscription link routing to the Shopify-hosted subscription management portal.
Discount enrichment
Order detail includes:
- Visible savings total in the summary
- Discount-code chips next to the summary row
- Discount badge on each affected line item
Header account chip
When a customer is logged in, the header swaps the account icon for a first-letter chip — the uppercase first letter of the customer's first name (or the first letter of their email address local-part if no first name is set).
The chip carries an aria-label with the full name for screen readers. It routes to routes.account_url. On mobile (viewports below 768 px), the icon stays for visual symmetry.
Testing your setup
To verify your setup, walk through each of these flows on a dev store:
Sign in + sign out
- Visit
/account/loginon a dev store. - Sign in with your customer credentials.
- Verify the header: the account icon should swap to a first-letter chip on desktop (≥ 768 px).
- Click the chip — the dashboard opens at
/accountwith the sidebar (Profile / Orders / Addresses / niche-quicklink / Privacy & data / Sign out). - Click
Sign out— redirects to home; chip reverts to icon.
Register
- Visit
/account/register. - Enter first name, last name, email, and a password (8+ characters).
- The password strength meter updates as you type (4 tiers: weak / fair / good / strong).
- Toggle show/hide on the password field — the input type swaps between password and text.
- Hit Caps Lock with focus in the password field — the caps-lock warning appears under the field.
- Submit. On success: redirect to
/accountwith a success toast (role='status', auto-dismiss after 5 seconds). - On error: the validation summary renders above the form; the first errored field gets focus; per-field
aria-invalid='true'+ error message renders.
Order detail (timeline + reorder)
- From the dashboard, click an order in the order-history table.
- The order-detail page renders the visual order timeline (3-step: Placed / Confirmed / Fulfilled) with
aria-current='step'on the current state and a pulsing accent border. - Cancelled order: the timeline collapses to a single Cancelled card.
- Click
Re-order. The button disables and shows a spinner; line items are added via CartAPI; the cart drawer opens. A success or partial-success toast appears.
Order detail (refund + subscription + print)
- Open an order that has a refund.
- Verify the refund panel renders below the line-items table with refund amount, date, and line items refunded.
- Open a subscription order.
- Verify the subscription card renders next to the line item with cadence label and manage-subscription link.
- Click
Print receipton any order — the browser print dialog opens with clean A4 output.
Addresses (country/province + delete)
- Click
Addressesin the sidebar. - Click
Add a new address. - Pick a country with provinces (US / Canada / India). Verify the province
<select>populates immediately. Switch to a country without provinces (Belgium / Singapore) — the province select hides. - Verify the locale-aware label updates:
ZIP codefor US,Postcodefor UK,PIN codefor India,Postal codefor Canada. - Save the address — locale-aware redirect with success toast.
- Delete an address — confirm prompt appears before submit.
Reset password (recovery email-sent surface)
- Visit
/account/login. - Click
Forgot your password?. The recover-email field surfaces inline on the same page. - Submit a known email. Shopify redirects to
/account/login?customer_posted=true. - Verify the recovery email-sent message renders distinct success messaging.
Sidebar privacy link
- Click
Privacy & datain the sidebar. - Verify the link routes to
shop.privacy_policy.url(your published policy) or/policies/privacy-policyif your dev store has the Shopify default. - Inspect the rendered
<a>— confirmrel='nofollow'is present.
Demo credentials
The following demo customer credentials are available for testing on the 5 dev stores:
| Preset | Storefront URL | Storefront password (dev-store gate) | Demo customer email | Demo customer password |
|---|---|---|---|---|
| Uisce | https://uisce.sionnach.solutions | uisce | uisce-customer@sionnach.solutions | uisce-customer-2026 |
| Gaoth | https://gaoth.sionnach.solutions | gaoth | gaoth-customer@sionnach.solutions | gaoth-customer-2026 |
| Talamh | https://talamh.sionnach.solutions | talamh | talamh-customer@sionnach.solutions | talamh-customer-2026 |
| Tine | https://tine.sionnach.solutions | tine | tine-customer@sionnach.solutions | tine-customer-2026 |
| Neart | https://neart.sionnach.solutions | neart | neart-customer@sionnach.solutions | neart-customer-2026 |
5 demo orders cover key test flows: order timeline in Confirmed state; order timeline in Fulfilled state with tracking; refund panel; cancelled order; and subscription/selling-plan card.
Before you go live
Confirm on at least one customer-account flow per preset.
Settings + configuration
- [ ] Customer-accounts mode chosen in Shopify admin → Settings → Customer accounts (Classic only / Classic+new / New only)
- [ ] Privacy policy published in Shopify admin → Settings → Policies (
shop.privacy_policy.urlresolves to a real URL, not 404) - [ ]
customer-account-main-menulinklist provisioned (or relying on the theme-default 5-item fallback — both reviewer-resilient) - [ ] Per-preset
niche_quicklink_urldefaults reviewed in theme editor → Templates → Customers > Account. - [ ] Per-preset metafield namespaces created in Shopify admin → Settings → Custom data (for the niche line-item blocks to render data)
Sign-in + register
- [ ] Live JS form validation: blur on empty
requiredfield →aria-invalid='true'+ error message visible - [ ] Email format validator: enter
not-an-email→ blur → error visible - [ ] Password strength meter (register): tier updates as you type (weak / fair / good / strong)
- [ ] Password show/hide toggle (register / activate / reset_password): button click flips
type='password'↔'text' - [ ] Caps-lock warning (password fields): hit Caps Lock with focus on a password input → warning visible under field
- [ ] Confirm-password match (register / activate / reset_password): mismatch sets
aria-invalid='true'on both fields; match clears both - [ ] Submit invalid form: first errored field receives focus + scrolls into view; summary
<div role='alert'>renders above form - [ ] Recovery email-sent surface:
/account/login?customer_posted=truerenders distinct success messaging instead of silent reload - [ ] Success toast (activate / reset / register):
<div role='status'>with auto-dismiss after 5 seconds
Account dashboard + sidebar nav
- [ ] Two-column layout on desktop ≥ 768 px: sidebar (~240 px) on inline-start, content on inline-end
- [ ] Mobile < 768 px: sidebar collapses to top-tab strip
- [ ] Sidebar consumes
linklists['customer-account-main-menu']when provisioned; falls back to 5 hard-coded links when not - [ ] Niche-quicklink slot renders in both branches (linklist-provisioned + theme-default)
- [ ] Privacy & data link routes to
shop.privacy_policy.url(or/policies/privacy-policyfallback) withrel='nofollow' - [ ] Active-state class on the link matching
window.location.pathname
Order detail
- [ ] Visual order timeline renders 3 steps (Placed / Confirmed / Fulfilled) with
aria-current='step'on current state - [ ] Cancelled order: timeline collapses to single Cancelled card with
order.cancelled_at+order.cancel_reason_label - [ ] Re-order CTA: button click → cart drawer opens with line items added; success toast composes;
cart:addevent emitted per line - [ ] Tracking link button (when
item.fulfillment.tracking_urlpresent): renders styled link withrel='noopener' target='_blank' - [ ] Print receipt button:
window.print()opens dialog;@media printstyles produce single-page A4 invoice - [ ] Refund panel: renders only when
order.refunds.size > 0 - [ ] Subscription card: renders only when
item.selling_plan_allocationtruthy - [ ] Per-preset niche line-item block renders (for example, spec summary on Tine; pet-type indicator on Gaoth)
Addresses
- [ ] Country/province dropdown: country change → province
<select>populates immediately - [ ] Province select hides when
country.provinces.size == 0 - [ ] Locale-aware label updates (ZIP code / Postcode / Postal code / PIN code) on country change
- [ ] Add address form: live JS validation + per-field
aria-invalid+role=alert - [ ] Delete address: confirm() prompt before submit
Header chip
- [ ] Logged-in: header renders first-letter chip
- [ ] Logged-out:
<shopify-account>Web Component preserved verbatim - [ ] Mobile < 768 px: chip reverts to icon (visual symmetry)
Localization + RTL
- [ ] All user-facing strings localize across your published locales
- [ ] Form labels use logical CSS properties (
padding-inline-start,margin-inline-start) - [ ] Order timeline:
flex-direction: row-reversein RTL — Placed appears on inline-end - [ ] Sidebar nav: moves to inline-end in RTL
- [ ] Strength meter: bar fills from inline-start (RTL fills from right)
Performance + accessibility
- [ ] No axe violations on
/account/login,/account,/account/orders/{id},/account/addresses - [ ] Lighthouse Accessibility ≥ 0.9 on customer-account templates
- [ ] Custom Element registration:
customer-form,country-province-selector,order-timeline,order-reorderall present in DevTools → Elements
Platform notes
Cancel-order on classic accounts
A cancel-order button is not available on classic accounts — Shopify does not expose a customer-side cancel API for the classic surface. New Customer Accounts handles cancellation via the Shopify-hosted portal.
Tracking events
The timeline shows Placed, Confirmed, and Fulfilled steps. The styled tracking-link button renders when tracking information is available — carrier name, tracking number, and a link to the carrier website. A richer per-event timeline widget is not feasible from the Shopify Storefront API, which provides tracking number, URL, and carrier only (no event stream).
Wishlist
No wishlist surface is included: no heart icon on product cards, no /pages/wishlist template, no sidebar Wishlist link. Cross-device sync requires customer.metafields scaffolding that is outside the current scope.
GDPR data-request form
The theme links to your published privacy policy — it does not implement the legal data-request form. Use a dedicated GDPR app or Shopify Functions for theme-side data-request forms when shopper volume warrants the build.
Related guides
- Cart guide — cart + checkout reference (the re-order CTA uses the same CartAPI interface)
- Search guide — search and blog reference
- Collection pages guide — collection page reference
- Product pages guide — product page reference
- Header guide — header reference (the first-name chip styling lives in
assets/header.css) - Translations (50 locales)
- Theme settings
- Color schemes