Chapter 18: Shopify Polaris UI

Embedded App Architecture

AdPriority runs as an embedded application inside the Shopify Admin. This means the app renders within an iframe managed by Shopify’s App Bridge, inheriting the admin’s navigation, authentication context, and visual language. Merchants never leave the Shopify Admin to use AdPriority.

App Bridge 4.1 Integration

App Bridge 4.1 provides the communication layer between AdPriority’s React frontend and the Shopify Admin host. It handles session token generation, navigation synchronization, and modal management.

EMBEDDED APP ARCHITECTURE
=========================

  +---------------------------------------------------------------+
  |  Shopify Admin (host)                                          |
  |                                                                |
  |  +---------------------------+  +----------------------------+ |
  |  | Admin Navigation          |  | Top Bar                    | |
  |  | - Home                    |  | - Store name               | |
  |  | - Orders                  |  | - Notifications            | |
  |  | - Products                |  | - Account                  | |
  |  | - Customers               |  +----------------------------+ |
  |  | - ...                     |                                 |
  |  | - Apps                    |  +----------------------------+ |
  |  |   +- AdPriority    <--------| App Bridge 4.1 iframe       | |
  |  |      |- Dashboard         |  |                            | |
  |  |      |- Products          |  |  React + Polaris v13       | |
  |  |      |- Rules             |  |  +----------------------+  | |
  |  |      |- Seasons           |  |  | AdPriority UI        |  | |
  |  |      |- Settings          |  |  | (renders here)       |  | |
  |  +---------------------------+  |  +----------------------+  | |
  |                                 +----------------------------+ |
  +---------------------------------------------------------------+

The entry point initializes App Bridge and wraps the application in the Polaris AppProvider:

// main.tsx
import { AppProvider } from "@shopify/polaris";
import { BrowserRouter } from "react-router-dom";
import enTranslations from "@shopify/polaris/locales/en.json";
import App from "./App";

function Root() {
  return (
    <AppProvider i18n={enTranslations}>
      <BrowserRouter>
        <App />
      </BrowserRouter>
    </AppProvider>
  );
}

Session tokens are extracted automatically by App Bridge and attached to every API request. The backend validates these JWTs using the Shopify client secret, confirming the merchant’s identity and shop context without cookies.


Polaris v13 Component Usage

AdPriority uses Polaris v13 exclusively for all UI elements. This guarantees visual consistency with the Shopify Admin and satisfies the App Store review requirement that embedded apps use Polaris.

Core Component Map

ComponentAdPriority Usage
PageTop-level page wrapper with title, breadcrumbs, primary action
LayoutTwo-column and single-column content sections
CardContent containers for each functional area
DataTableProduct list with sortable columns
BadgePriority level indicators (color-coded 0-5)
ButtonPrimary, secondary, and destructive actions
ModalConfirmation dialogs for bulk operations and overrides
BannerSync status alerts, errors, onboarding messages
SelectPriority level dropdown, season picker, filter selectors
TextFieldSearch, manual override reason, rule pattern input
NavigationMenuApp-level navigation (Dashboard, Products, Rules, Seasons, Settings)
ProgressBarSync progress indicator
SpinnerLoading states for data fetches
EmptyStateFirst-run experience when no products or rules exist
TabsSub-navigation within pages (e.g., product filters)
IndexTableAlternative to DataTable for selectable product rows
FiltersApplied filter tags for product list refinement

Priority Badge Component

The priority badge is the most frequently used custom component. It maps the 0-5 score to Polaris Badge tones:

PRIORITY BADGE MAPPING
======================

  Score   Badge Tone      Label          Color
  -----   ----------      -----          -----
    5      "success"       "5 - Push"     Green
    4      "info"          "4 - Strong"   Teal
    3      "attention"     "3 - Normal"   Yellow
    2      "warning"       "2 - Low"      Orange
    1      "critical"      "1 - Minimal"  Red (light)
    0      "new"           "0 - Exclude"  Grey
// components/PriorityBadge.tsx
import { Badge } from "@shopify/polaris";

const PRIORITY_CONFIG = {
  5: { tone: "success",   label: "5 - Push Hard" },
  4: { tone: "info",      label: "4 - Strong" },
  3: { tone: "attention",  label: "3 - Normal" },
  2: { tone: "warning",   label: "2 - Low" },
  1: { tone: "critical",  label: "1 - Minimal" },
  0: { tone: "new",       label: "0 - Exclude" },
} as const;

export function PriorityBadge({ priority }: { priority: number }) {
  const config = PRIORITY_CONFIG[priority as keyof typeof PRIORITY_CONFIG];
  return <Badge tone={config.tone}>{config.label}</Badge>;
}

Key Screens

Screen 1: Dashboard

The dashboard is the landing page after app installation. It provides a high-level overview of the priority distribution across the product catalog, the last sync status, and quick action buttons.

+------------------------------------------------------------------+
| AdPriority                                              [Sync Now]|
+------------------------------------------------------------------+
|                                                                    |
|  +---------------------------+  +-------------------------------+  |
|  | Priority Distribution     |  | Sync Status                  |  |
|  |                           |  |                               |  |
|  |       [PIE CHART]         |  | Shopify:  2 min ago    [ok]  |  |
|  |                           |  | Sheet:    1 hour ago   [ok]  |  |
|  |  5 - Push:     312  13%  |  | GMC:      Daily at 2AM [ok]  |  |
|  |  4 - Strong:   487  20%  |  |                               |  |
|  |  3 - Normal:   824  34%  |  | Next scheduled sync:         |  |
|  |  2 - Low:      401  17%  |  | Today at 3:00 PM             |  |
|  |  1 - Minimal:  198   8%  |  |                               |  |
|  |  0 - Exclude:  203   8%  |  +-------------------------------+  |
|  |                           |                                    |
|  +---------------------------+  +-------------------------------+  |
|                                 | Quick Stats                   |  |
|  +---------------------------+  |                               |  |
|  | Recent Activity           |  | Total Products:     2,425    |  |
|  |                           |  | Active in GMC:     15,284    |  |
|  | 10:30 - Season changed    |  | Needs Attention:       47    |  |
|  |   to Winter               |  | New Arrivals:          12    |  |
|  | 10:28 - 47 products       |  | Rules Active:          20    |  |
|  |   updated via rules       |  |                               |  |
|  | 09:15 - Sync completed    |  | [View Products]              |  |
|  |   (2,425 ok, 0 errors)   |  | [Manage Rules]               |  |
|  |                           |  +-------------------------------+  |
|  +---------------------------+                                    |
+------------------------------------------------------------------+

Layout: Layout with two-column Layout.Section pairs. The pie chart uses Chart.js rendered inside a Card. Each stat card uses Polaris Text components with variant="headingLg" for the numbers.

Quick Actions:

  • “Sync Now” button (primary action on the Page component)
  • “View Products” link navigates to the Products screen
  • “Manage Rules” link navigates to the Rules screen

“Needs Attention” count highlights products with priority 0 that still have inventory, meaning they could be advertised but are currently excluded.

Screen 2: Products List

The products list is the primary working screen. It displays every active product with its current priority score, category group, seasonal context, and sync status.

+------------------------------------------------------------------+
| Products                                   [Recalculate All] [...]|
+------------------------------------------------------------------+
| Filters: [Priority: All v] [Type: All v] [Status: All v] [Search]|
| Applied: Priority >= 3  x  |  Type: Outerwear  x                 |
+------------------------------------------------------------------+
| [ ] Product            | Type           | Vendor      | Priority  |
|     Title              |                |             | Score     |
|------------------------------------------------------------------+
| [ ] Jordan Craig       | Stacked Jeans  | Jordan Craig| [5-Push]  |
|     Stacked Jeans      |                |             | seasonal  |
|------------------------------------------------------------------+
| [ ] Rebel Minds Puffer | Puffer Jackets | Rebel Minds | [5-Push]  |
|     Jacket Camo        |                |             | seasonal  |
|------------------------------------------------------------------+
| [ ] New Era Yankees    | Baseball-Fitted| New Era     | [4-Strong]|
|     59FIFTY Navy       |                |             | rule+tag  |
|------------------------------------------------------------------+
| [ ] Ethika Men Go      | Men-Underwear  | Ethika      | [2-Low]   |
|     Pac Go             |                |             | default   |
|------------------------------------------------------------------+
| [ ] G3 Patriots        | Hoodies &      | G-III Sports| [0-Excl]  |
|     Waffled Hoodie     | Sweatshirts    |             | tag:DEAD50|
+------------------------------------------------------------------+
| Showing 1-50 of 2,425     [<] [1] [2] [3] ... [49] [>]           |
+------------------------------------------------------------------+
| Selected: 3 products   [Set Priority...] [Recalculate] [Export]   |
+------------------------------------------------------------------+

Columns:

ColumnSourceSortable
Product (title + image thumbnail)Shopify product dataYes (alphabetical)
Typeproduct_type fieldYes
Vendorvendor fieldYes
PriorityCalculated score with PriorityBadgeYes (numeric)
SeasonCurrent season labelNo
Statussync_status (synced/pending/error)Yes
ActionsEdit button, overflow menuNo

Filters (using Polaris Filters component):

  • Priority: Dropdown with 0-5, or “All”
  • Type: Dropdown populated from category groups (T-Shirts, Jeans & Pants, etc.)
  • Status: Synced, Pending, Error
  • Search: Free-text search across title, vendor, SKU

Bulk Actions (appear when rows are selected via IndexTable):

  • “Set Priority” – Opens modal with priority selector and reason field
  • “Recalculate” – Re-runs the scoring engine for selected products
  • “Export” – Downloads selected products as CSV

Pagination: Server-side, 50 products per page. Uses cursor-based pagination for performance with large catalogs.

Screen 3: Product Detail

Clicking a product row opens the detail view. This screen shows the full priority calculation breakdown: what the base score is, which modifiers applied, and the resulting final score.

+------------------------------------------------------------------+
| < Back to Products                                                |
| Jordan Craig Stacked Jeans                            [Save] [...]|
+------------------------------------------------------------------+
|                                                                    |
|  +---------------------------+  +-------------------------------+  |
|  | Product Info              |  | Priority Breakdown            |  |
|  |                           |  |                               |  |
|  | Title: Jordan Craig       |  | Category: Jeans & Pants       |  |
|  |   Stacked Jeans           |  | Base priority:           4    |  |
|  | Type: Men-Bottoms-         |  |                               |  |
|  |   Stacked Jeans           |  | Seasonal (Winter):       4    |  |
|  | Vendor: Jordan Craig      |  |                               |  |
|  | Tags: jordan-craig,       |  | Tag: NAME BRAND         +1   |  |
|  |   NAME BRAND, in-stock    |  | Tag: in-stock            +1   |  |
|  | Created: 2025-09-15       |  | (capped at 5)                |  |
|  | Variants: 12              |  |                               |  |
|  | Inventory: 48 total       |  | ================================|  |
|  |                           |  | Final Priority:    [5-Push]   |  |
|  +---------------------------+  | Source: seasonal + tags        |  |
|                                 |                               |  |
|  +---------------------------+  | [ ] Lock this priority        |  |
|  | Manual Override           |  +-------------------------------+  |
|  |                           |                                    |
|  | Priority: [5 v]           |  +-------------------------------+  |
|  | Reason:  [____________]   |  | Custom Labels (GMC)          |  |
|  | [Apply Override]          |  |                               |  |
|  |                           |  | label_0: priority-5           |  |
|  | Note: Overrides all       |  | label_1: winter               |  |
|  | rules until unlocked.     |  | label_2: jeans-pants          |  |
|  +---------------------------+  | label_3: in-stock             |  |
|                                 | label_4: name-brand           |  |
|                                 |                               |  |
|                                 | Last synced: 2 hours ago      |  |
|                                 +-------------------------------+  |
+------------------------------------------------------------------+

Priority Breakdown Card: Shows each step of the calculation pipeline in order. The merchant can see exactly why a product received its score, which eliminates the “black box” concern.

Manual Override Card: Contains a Select dropdown (0-5), a TextField for the reason (required when overriding), and an “Apply Override” Button. When applied, the product’s priority_locked flag is set to true, and no automated rules will change the score until the lock is removed.

Custom Labels Card: Displays the exact values that will be (or have been) synced to Google Merchant Center. This provides transparency into what Google Ads campaigns will see.

Screen 4: Rules Engine

The rules engine screen lists all category rules and provides a form for creating and editing rules. Rules define the base priority for product type patterns and their seasonal modifiers.

+------------------------------------------------------------------+
| Rules                                                  [Add Rule] |
+------------------------------------------------------------------+
|                                                                    |
|  +--------------------------------------------------------------+  |
|  | Active Rules (20)                                    [Reorder]|  |
|  |--------------------------------------------------------------|  |
|  | #  | Name               | Pattern          | Base | Products |  |
|  |----|--------------------|---------—--------|------|----------|  |
|  | 1  | Outerwear - Heavy  | *Puffer*,        |  5   |    35    |  |
|  |    |                    | *Shearling*      |      |          |  |
|  | 2  | Hoodies & Sweats   | *Hoodies*,       |  3   |   264    |  |
|  |    |                    | *Sweatshirts*    |      |          |  |
|  | 3  | T-Shirts           | Men-Tops-T-Shirt*|  3   |  1,363   |  |
|  | 4  | Jeans & Pants      | *Pants-Jeans,    |  4   |   911    |  |
|  |    |                    | *Stacked Jeans   |      |          |  |
|  | 5  | Shorts             | *Shorts*         |  3   |   315    |  |
|  | ...                                                           |  |
|  | 20 | Exclude            | Bath & Body,     |  0   |    50    |  |
|  |    |                    | Gift Cards, ...  |      |          |  |
|  +--------------------------------------------------------------+  |
|                                                                    |
+------------------------------------------------------------------+

Create / Edit Rule Form (opens in a modal or inline section):

+------------------------------------------------------------------+
| Create Rule                                                       |
+------------------------------------------------------------------+
|                                                                    |
|  Rule Name:     [Outerwear - Heavy______________]                  |
|  Description:   [Winter outerwear, peak demand___]                 |
|                                                                    |
|  Product Type Pattern:                                             |
|  [*Puffer Jackets, *Shearling___________________]                  |
|  (comma-separated, supports * wildcard)                            |
|                                                                    |
|  Base Priority: [5 - Push Hard  v]                                 |
|                                                                    |
|  Seasonal Modifiers:                                               |
|  +-------------+----------+                                        |
|  | Season      | Priority |                                        |
|  |-------------|----------|                                        |
|  | Winter      | [5 v]    |                                        |
|  | Spring      | [1 v]    |                                        |
|  | Summer      | [0 v]    |                                        |
|  | Fall        | [4 v]    |                                        |
|  +-------------+----------+                                        |
|                                                                    |
|  [ ] Active                                                        |
|                                                                    |
|  Matching Products: 35 (preview)                                   |
|                                                                    |
|                                       [Cancel]  [Save Rule]       |
+------------------------------------------------------------------+

The pattern field supports comma-separated product type strings with * wildcard matching. When the merchant types a pattern, a live preview shows how many products match. This provides immediate feedback before saving.

Rules are evaluated in order (drag-to-reorder supported). The first matching rule wins, so more specific rules should be placed higher in the list.

Screen 5: Seasonal Calendar

The seasonal calendar provides a visual representation of season boundaries across the year, with a matrix editor for category-season priority assignments.

+------------------------------------------------------------------+
| Seasons                                                [Add Season]|
+------------------------------------------------------------------+
|                                                                    |
|  +--------------------------------------------------------------+  |
|  | Calendar View                                                 |  |
|  |                                                               |  |
|  | Jan  Feb  Mar  Apr  May  Jun  Jul  Aug  Sep  Oct  Nov  Dec   |  |
|  | [====WINTER====][=====SPRING=====][=====SUMMER=====][==FALL==]|  |
|  | Dec 1 - Feb 28  Mar 1 - May 31   Jun 1 - Aug 31  Sep1-Nov30 |  |
|  |                                                               |  |
|  | Drag boundaries to adjust dates                               |  |
|  +--------------------------------------------------------------+  |
|                                                                    |
|  +--------------------------------------------------------------+  |
|  | Category x Season Matrix                          [Edit Mode] |  |
|  |--------------------------------------------------------------|  |
|  | Category          | Winter | Spring | Summer | Fall | Default |  |
|  |-------------------|--------|--------|--------|------|---------|  |
|  | T-Shirts          |   2    |   4    |   5    |  3   |    3    |  |
|  | Jeans & Pants     |   4    |   4    |   3    |  5   |    4    |  |
|  | Shorts            |   0    |   3    |   5    |  1   |    3    |  |
|  | Hoodies & Sweats  |   5    |   3    |   1    |  5   |    3    |  |
|  | Outerwear - Heavy |   5    |   1    |   0    |  4   |    3    |  |
|  | Outerwear - Medium|   4    |   3    |   0    |  4   |    3    |  |
|  | Outerwear - Light |   2    |   4    |   1    |  3   |    3    |  |
|  | Headwear - Caps   |   3    |   3    |   3    |  3   |    3    |  |
|  | Headwear - Cold   |   5    |   1    |   0    |  3   |    2    |  |
|  | Headwear - Summer |   0    |   3    |   4    |  2   |    2    |  |
|  | Joggers           |   4    |   3    |   2    |  4   |    3    |  |
|  | Underwear & Socks |   2    |   2    |   2    |  2   |    2    |  |
|  | Accessories       |   2    |   2    |   2    |  2   |    2    |  |
|  | Footwear - Sandals|   0    |   2    |   5    |  0   |    2    |  |
|  | Footwear - Shoes  |   3    |   3    |   3    |  3   |    3    |  |
|  | Swim Shorts       |   0    |   2    |   5    |  0   |    2    |  |
|  | Women - Apparel   |   2    |   3    |   3    |  2   |    2    |  |
|  | Exclude           |   0    |   0    |   0    |  0   |    0    |  |
|  +--------------------------------------------------------------+  |
|                                                                    |
|  Current Season: Winter (Dec 1 - Feb 28)                           |
|  Next Transition: Spring on Mar 1 (19 days)                        |
|                                                                    |
+------------------------------------------------------------------+

Calendar View: A horizontal bar chart showing the four season blocks across 12 months. In edit mode, the season boundaries become draggable handles. Adjusting a boundary updates the seasons table dates and triggers a recalculation preview showing how many products would change priority.

Category x Season Matrix: A grid where each cell is a Select dropdown (0-5). Cells are color-coded to match the priority badge scheme (green for 5, grey for 0). Editing a cell updates the corresponding season_rules record.

Transition Countdown: Displayed below the matrix, showing the current active season and days until the next automatic transition. When a transition is imminent (within 7 days), a Banner with tone warning appears at the top of the page.

Screen 6: Settings

The settings screen manages GMC connection details, sync configuration, and notification preferences.

+------------------------------------------------------------------+
| Settings                                                   [Save] |
+------------------------------------------------------------------+
|                                                                    |
|  +--------------------------------------------------------------+  |
|  | Google Merchant Center                                        |  |
|  |                                                               |  |
|  | Connection Status: Connected [checkmark]                      |  |
|  | Merchant ID:    [123456789________]                           |  |
|  | Sync Method:    [Google Sheets v]                             |  |
|  | Sheet URL:      [https://docs.google.com/spreadsheets/d/...] |  |
|  |                                                               |  |
|  | [Test Connection]  [Disconnect]                               |  |
|  +--------------------------------------------------------------+  |
|                                                                    |
|  +--------------------------------------------------------------+  |
|  | Sync Schedule                                                 |  |
|  |                                                               |  |
|  | Frequency:      [Every 6 hours v]                             |  |
|  | Options:        Every hour / Every 6 hours / Daily / Manual   |  |
|  |                                                               |  |
|  | Auto-sync on product changes:  [x] Enabled                   |  |
|  | Auto-sync on season transition: [x] Enabled                  |  |
|  +--------------------------------------------------------------+  |
|                                                                    |
|  +--------------------------------------------------------------+  |
|  | Priority Defaults                                             |  |
|  |                                                               |  |
|  | Default priority for unmapped types: [3 - Normal v]           |  |
|  | New arrival boost:                   [x] Enabled              |  |
|  | New arrival duration:                [14] days                |  |
|  | New arrival priority:                [5 - Push Hard v]        |  |
|  +--------------------------------------------------------------+  |
|                                                                    |
|  +--------------------------------------------------------------+  |
|  | Notifications                                                 |  |
|  |                                                               |  |
|  | Email on sync failure:       [x] Enabled                     |  |
|  | Email on season transition:  [x] Enabled                     |  |
|  | Notification email:          [will@nexusclothing.com]         |  |
|  +--------------------------------------------------------------+  |
|                                                                    |
+------------------------------------------------------------------+

GMC Connection Card: The Google Sheets URL is the primary configuration. A “Test Connection” button verifies the Sheets API can write to the specified spreadsheet. For Pro tier merchants using the Content API directly, this card shows OAuth connection status instead.

Sync Schedule Card: Controls how often AdPriority pushes updated labels to the Google Sheet. The frequency dropdown determines the cron interval for the background worker job.

Priority Defaults Card: Sets the fallback priority for products whose product type does not match any rule, and configures the new arrival boost behavior.

Screen 7: Billing

The billing screen shows the current subscription plan, usage against limits, and upgrade/downgrade options. It integrates with the Shopify Billing API.

+------------------------------------------------------------------+
| Billing                                                           |
+------------------------------------------------------------------+
|                                                                    |
|  +--------------------------------------------------------------+  |
|  | Current Plan: Growth ($79/mo)                                 |  |
|  |                                                               |  |
|  | Status:    Active                                             |  |
|  | Billing:   Monthly (via Shopify)                              |  |
|  | Next bill: March 10, 2026                                     |  |
|  | Products:  2,425 / Unlimited                                  |  |
|  |                                                               |  |
|  | Features included:                                            |  |
|  | [ok] Unlimited products                                       |  |
|  | [ok] Seasonal automation                                      |  |
|  | [ok] Rules engine                                             |  |
|  | [ok] Tag modifiers                                            |  |
|  | [ok] New arrival boost                                        |  |
|  | [--] Google Ads integration (Pro)                             |  |
|  | [--] AI recommendations (Pro)                                 |  |
|  | [--] ROAS tracking (Pro)                                      |  |
|  +--------------------------------------------------------------+  |
|                                                                    |
|  +-------------------+  +-------------------+  +-----------------+ |
|  | Starter  $29/mo   |  | Growth   $79/mo   |  | Pro    $199/mo  | |
|  |                   |  | (current)         |  |                 | |
|  | 500 products      |  | Unlimited         |  | Unlimited       | |
|  | Manual scoring    |  | Seasonal auto     |  | Google Ads API  | |
|  | GMC sync          |  | Rules engine      |  | AI suggestions  | |
|  |                   |  | Tag modifiers     |  | ROAS tracking   | |
|  |                   |  |                   |  | Performance     | |
|  |                   |  |                   |  |   dashboard     | |
|  | [Downgrade]       |  | Current Plan      |  | [Upgrade]       | |
|  +-------------------+  +-------------------+  +-----------------+ |
|                                                                    |
+------------------------------------------------------------------+

Upgrade and downgrade actions redirect to the Shopify billing confirmation page via the appSubscriptionCreate GraphQL mutation. Shopify handles payment collection, proration, and cancellation natively.


AdPriority uses the Polaris NavigationMenu component (provided by App Bridge) to register its navigation items with the Shopify Admin sidebar. This places the app’s pages directly in the admin navigation when the merchant is inside the app.

APP NAVIGATION STRUCTURE
========================

  AdPriority
  |
  +-- Dashboard        /          Overview, stats, quick actions
  +-- Products         /products   Product list, priorities, bulk ops
  +-- Rules            /rules      Category rules, rule builder
  +-- Seasons          /seasons    Seasonal calendar, matrix editor
  +-- Settings         /settings   GMC config, sync, notifications
// App.tsx
import { NavMenu } from "@shopify/app-bridge-react";
import { Routes, Route } from "react-router-dom";

export default function App() {
  return (
    <>
      <NavMenu>
        <a href="/" rel="home">Dashboard</a>
        <a href="/products">Products</a>
        <a href="/rules">Rules</a>
        <a href="/seasons">Seasons</a>
        <a href="/settings">Settings</a>
      </NavMenu>
      <Routes>
        <Route path="/" element={<Dashboard />} />
        <Route path="/products" element={<Products />} />
        <Route path="/products/:id" element={<ProductDetail />} />
        <Route path="/rules" element={<Rules />} />
        <Route path="/seasons" element={<Seasons />} />
        <Route path="/settings" element={<Settings />} />
        <Route path="/billing" element={<Billing />} />
      </Routes>
    </>
  );
}

The billing page is intentionally omitted from the main navigation since merchants access it infrequently. A link to it is placed in the Settings page and in the Dashboard’s plan status card.


Responsive Design and Accessibility

Polaris v13 handles responsive layout automatically. All Polaris components adapt to the embedded iframe width, which varies based on the merchant’s browser window size. The Layout component switches from two-column to single-column at narrow widths.

Accessibility is built into Polaris at the component level, meeting WCAG 2.0 AA standards. AdPriority adds the following custom accessibility considerations:

ConsiderationImplementation
Priority colorsNever rely on color alone; badge text always includes the numeric score
Data tablesUse proper th scope and aria-sort attributes via Polaris DataTable
ModalsFocus trap and keyboard navigation handled by Polaris Modal
Loading statesaria-busy on containers during data fetch, Spinner with label text
Error messagesBanner with tone="critical" and descriptive text, linked to the failed field

State Management

AdPriority uses React Query (TanStack Query) for server state and React context for local UI state. There is no global client-side store (no Redux, no Zustand). The rationale is that AdPriority’s state is almost entirely server-derived: product data, rules, seasons, and sync status all originate from the backend API.

STATE MANAGEMENT PATTERN
========================

  React Query (Server State)         React Context (UI State)
  +---------------------------+      +-------------------------+
  | useProducts()             |      | FilterContext            |
  |   - product list          |      |   - selected priority   |
  |   - pagination cursor     |      |   - selected type       |
  |   - refetch on mutation   |      |   - search query        |
  |                           |      |                         |
  | useRules()                |      | SelectionContext         |
  |   - rule list             |      |   - selected row IDs    |
  |   - create/update/delete  |      |   - bulk action state   |
  |                           |      |                         |
  | useSeasons()              |      | ToastContext             |
  |   - season config         |      |   - success/error msgs  |
  |   - matrix data           |      |                         |
  |                           |      |                         |
  | useSyncStatus()           |      |                         |
  |   - polling every 30s     |      |                         |
  +---------------------------+      +-------------------------+

React Query’s stale-while-revalidate pattern ensures the UI always shows data immediately from cache while fetching fresh data in the background. Mutations (priority changes, rule edits) optimistically update the cache and roll back on server error.