Changelog
All notable changes to this store, one entry per merged pull request.
-
feat
#126
turn the error page into a recovery page
The 404 rendered a
product-listbound tocollection: all. That collection is unsorted, so the first products shown were papan bunga condolence boards — a visitor who hit a broken link was met with a funeral wreath reading *"Turut Berduka"*, in yellow and blue, on a brand positioned as premium editorial.That is the most off-brand surface on the site, shown to people already having a bad experience.
-
fix
#125
correct delivery coverage to nationwide, not Jabodetabek
The About page claimed delivery "across Jabodetabek" in three places, including the
FAQPageJSON-LD that Google reads. Outerbloom ships nationwide across Indonesia, with same-day delivery nationwide for fresh flowers and papan bunga.This was factually wrong, materially understated coverage, and was live in structured data. My error from the earlier About build.
-
feat
#124
publish open roles to Google Jobs via JobPosting schema
The careers page lists six open roles that were invisible to Google Jobs — a surface candidates search directly. This adds
JobPostingstructured data for each. -
feat
#123
signature pieces, preserved-flower pillars, FAQ schema, corporate bridge
Deepens the About page using the Bloom Book — the brand's primary branding asset, which was almost entirely unused on the site.
-
fix
#119
add honeypot + time-trap spam guard to partner forms
- Both the CP Lead Form and CP Contact form (corporate partnership pages) submit straight to the API with zero bot protection, and have been getting hit by automated form spam.
- Add a hidden honeypot field (
cp_hp) and a minimum-fill-time check (3s) to each form. - A submission tripping either guard is silently dropped without hitting the API — no company row, no notification — so bots get no signal to adapt against.
-
feat
#120
add native replacement for the HubSpot embed form
/pages/be-our-partnercurrently embeds a HubSpot Free Forms widget (Nama Pemilik, Nama Perusahaan, Website/IG/Facebook, Email, No Telp (WA), Pesan) with no spam protection, and it's been getting hit hard by bot spam.- Add a new
be-our-partner-formsection replicating the same fields/copy, protected with the same honeypot + time-trap guard used on the CP Lead Form and CP Contact form (see #119). - Add
page.be-our-partnertemplate so it can be assigned to the page from the theme…
-
fix
#122
stop mobile horizontal overflow in proof-points grid
The About page scrolls sideways on mobile. Document width is 1520px on a 390px viewport — about 1130px of overflow.
-
feat
#121
rebuild Tentang Kami as English editorial About page
Rebuilds
/pages/tentang-kamias a proper About page using native Horizon sections, replacing the emptypage.aboutplaceholder template. -
fix
#118
accept the shop's custom domain in the checkout-URL safety check
Found via live end-to-end testing of #117 with a real Storefront API token against the production store (
outerbloom1.myshopify.com, custom domainouterbloom.com) -- before thestorefront_api_tokensetting was ever turned on in production.cartCreate'scheckoutUrlcomes back on the shop's primary/custom domain (outerbloom.com), notshop.permanent_domain(outerbloom1.myshopify.com).isSafeCheckoutUrlonly allowlisted the latter, so it would have rejected every… -
feat
#117
client-side Storefront API prefill, replacing web-raku as primary
Recreated from #116, which GitHub permanently closed when the base branch (
fix/checkout-city-lookup-shadowed-by-plain-product, now merged as #115) was deleted -- GitHub refuses to reopen or retarget a PR once its base branch is gone. Same commit, same content, now based directly onmain(which already includes #115).Adds a client-side Storefront API
cartCreateas the new primary checkout-address-prefill mechanism, ahead of the existing externalweb-raku… -
fix
#115
stop plain products from shadowing the delivery-city lookup
Regression from #114 (merged 2026-08-10).
decideCheckoutNavigation()inassets/checkout-fallback.jspicks the cart line to read the delivery address from by taking the last item withproperties.type === 'product'. Before #114, only lines that actually carried personalization/delivery data ever gottype: 'product'. #114 made that tag unconditional on every add-to-cart, so a plain product (e.g. jewelry) added to the cart *after* a bouquet/board now also matches… -
feat
#114
tag every add-to-cart with properties[type]=product
- Extends the existing PatchedFormData bridge in
blocks/buy-buttons.liquid(previously only fired for papan-bunga personalized products) so every add-to-cart form submission always carriesproperties[type]=product. - Adds the same
properties: { type: 'product' }to the JSON batch-add path inassets/product-form.js(used for queued variant-change adds). - Leaves
assets/pb-card.js's companion greeting-card line item astype: 'card'untouched, sincecheckout-fallback.js…
- Extends the existing PatchedFormData bridge in
-
feat
#113
jarring separation between role accordions
Follow-up to #112 — the accordion shipped there still read as a soft list; direct feedback was "separation between each roles is still not there... make it more obvious and jarring."
- Divider between roles: 1px hairline -> 3px solid ink rule (the hairline was the same weight as the internal dividers used for skill tags / nice-to-have, so it didn't register as a boundary between roles).
- Vertical padding around each role's summary: 56/88px (was 40/56px).
- Added a large ghost index…
-
feat
#112
accordion roles, bigger titles, richer JD
Replaces #111 (dedicated sub-page per role, closed unmerged) with an in-page accordion, per direction change after reviewing a screenshot of the sub-page approach.
- Each role is now a native
<details>/<summary>accordion (no JS, accessible by default) instead of a plain list row. - Title size: 18/20px -> 26/32px, plus a new department/location/type eyebrow line above it. Each role reads as its own section now, not a row in a dense list — this was the specific UX gap flagged in…
- Each role is now a native
-
feat
#110
homepage-matched tokens, candidate-facing JD, role anchors
- Replaces the placeholder palette from
KARIR_REDESIGN_PLAN.mdwith the real homepage design tokens (#212121ink /#6b6b68muted /#e2e4e6hairline /#640000accent /#fcfaf7warm surface) and matching H2–H6 type scale, across all 5 career sections. - Removes the "Why Outerbloom" proof row (
career-proof.liquid, deleted). It sold the brand to customers (same-day delivery, city count) — not relevant to a candidate. Flagged by @wilthung during review. - Improves the role JD,…
- Replaces the placeholder palette from
-
revert
#109
pull the same-day product-card badge from live
- Same-day badge ("Bisa hari ini", A3 / BBB2-6579) shipped to production today via PR #105, bundled inside commit
d4cb86dwith three unrelated fixes. - It needs deeper team review before being live (merchant-tag semantics, badge design/copy sign-off) — this reverts just the badge.
- WhatsApp enquiry (D1), the
customElements.defineguards, and the cart display hygiene fix (B4) from that same commit are untouched.
- Same-day badge ("Bisa hari ini", A3 / BBB2-6579) shipped to production today via PR #105, bundled inside commit
-
#108
Karir page: editorial redesign — bespoke sections, warm palette
- Full redesign of
/pages/karir, this time as bespoke Liquid sections rather than Horizongroup/textblocks — following the existingsections/corporate-partner-*.liquidpattern already in this repo. - Root cause fixed:
config/settings_data.jsonsets--font-h1..h4--case: uppercaseat theme level. This silently overrode the sentence-case headline shipped in #107 — the live page kept shouting no matter what copy was set. Bespoke markup is the only way to actually own text…
- Full redesign of
-
#107
Karir page: full revamp — spacious role cards, punchier copy
- Full UI/UX revamp of
/pages/karir, aimed at a more modern, premium feel and a more direct/confident brand voice per request. - Positions (the main change): replaced the accordion job listings with distinct bordered cards — department pill, big role title, one confident left-aligned "Apply Now" CTA that goes full-width on mobile. Each role now gets real visual separation instead of collapsed rows. Responsibilities/requirements/apply links carried over verbatim — only…
- Full UI/UX revamp of
-
#106
Karir page: illustrated values icons (from FAVO career page)
- Replaces the generic Shopify icon-in-circle badge on the
/pages/karir"Our Values" cards with custom line-art illustrations (fist bump / growth chart / rice stalk), adapted from favo.id/pages/career and recolored to Outerbloom's monochrome brand black (#212121) to match the site's existing "modern & sophisticated" palette. - Removed the 64px black circle badge treatment in the page's custom CSS (
career-stylessection) — it was sized for tiny glyphs and would have clipped the…
- Replaces the generic Shopify icon-in-circle badge on the
-
docs
#103
track the Jira export and Notion migration tooling
The Outerbloom Horizon backlog — epic BBB2-6557, 46 issues — is moving to Notion so Anggia, Putri and Dona can own their own work, and the Jira issues will be deleted afterwards. These files are what make that safe to do.
Documentation and tooling only. Nothing here is served by the theme, and it touches no Liquid, JS, or settings.
-
feat
#105
WhatsApp enquiry, same-day card badge, custom-element guards, cart polish
_Recreated from #102 — GitHub auto-closed it when #104's base branch was deleted on merge. Same branch/commits, retargeted to
main._ -
feat
#104
show the delivery promise on PDPs and delivery facts in the cart
_Recreated from #101 — GitHub auto-closed it when #100's base branch was deleted on merge. Same branch/commits, retargeted to
main._ -
fix
#99
Safari/in-app-webview resilience + PDP/collection image loading (audit quick wins)
The 2026-08-09 migration audit (qa-system/docs/AUDIT_HANDOFF.md) found that both purchase flows work, but they leak revenue at the edges: the checkout button dies silently on storage-restricted browsers (exactly where Instagram/WhatsApp traffic lives), and eager image loading pushes the papan bunga PDP to a 19.4s lab LCP. This PR is the small, high-confidence, independently-revertible subset — no template JSON, no settings, no app code.
-
fix
#100
escape braces so a PR body quoting Liquid can't break the theme
/changelogis broken on the live theme, andshopify theme devrefuses to serve any page at all for anyone on the team. Shopify rejects the *entire theme upload* when one Liquid file has a syntax error:``
Liquid syntax error (line 116): Variable '{{ request.origin | append: '/search?q={search_term_string}' was not properly terminated``I hit this trying to preview an unrelated change locally — every route returned 500.
-
feat
#98
add smooth floating checkout button on mobile cart page
On the mobile cart page, a long items list pushes the checkout button below the fold with no way to reach it without scrolling all the way down.
-
fix
#95
correct trust-badge typos on product and collection pages
Two copy typos in the trust-badge strip that renders on every product and collection page, directly above the buy area:
| Was | Now | Occurrences | |---|---|---| |
PREPARED WITH CARED|PREPARED WITH CARE| 3 | |ON- TIME DELIVERY(stray space) |ON-TIME DELIVERY| 6 |Files:
templates/product.json,templates/product.ff.json,templates/product.papan-bunga.json,templates/collection.json,templates/collection.campaign.json -
fix
#97
detect active variant via Horizon's variant:update event
- Custom Variant block (
blocks/ai_gen_block_477e93e.liquid) detected the active variant by pollinginput[name=\"id\"]every 400ms — a Dawn-era pattern. In this theme's Horizon web-component product form, that input only updates asynchronously after thevariant:updateevent fires, so the block kept using the initially-loaded variant title and the Photo field never appeared for variants like "Custom logo & photo for frame". - Replaced the polling/DOM-reading logic with a…
- Custom Variant block (
-
feat
#94
drive carousel via theme slideshow
Section collection menu list (di halaman collection) sekarang memakai fitur slideshow yang sama dengan section *featured collection product* di homepage.
Sebelumnya section ini punya carousel custom (
collection-menu-carousel+ overflow-scroll). Sekarang dirender lewat snippet bersamaresource-list— jalur yang sama dipakai sectionproduct-listdi homepage — sehingga slider mobile menjadislideshow-componentbawaan tema: snap antar-slide, panah navigasi, peek slide… -
fix
#93
stop the carousel row from claiming vertical touches
Reported again after PR #91: the mobile "Our Selections" collection carousel still traps vertical touch scrolling — the shopper has to touch outside the carousel section to scroll the page up/down.
-
fix
#92
raise BitChat launcher on ALL product pages (personalize CTA overlap)
Follow-up dari #91. Setelah #91 di-merge, launcher chat BitChat masih menutupi tombol "PERSONALIZE THIS BOARD/BOUQUET" di product mobile.
-
fix
#91
raise BitChat launcher above sticky bar + free collection carousel vertical scroll
Dua perbaikan UX mobile:
-
fix
#90
drop featured content from the mobile menu
The mobile menu drawer no longer renders
menu-drawer__featured-content— the horizontal strip of collection/product cards that appeared at the bottom of the drawer and again inside each expanded submenu.One file,
snippets/header-drawer.liquid, two guards. -
fix
#89
use base link colour, size read-more with body preset
Two fixes to the Collection description section, both driven by settings the merchant already controls.
-
perf
#88
serve images sized to their rendered box
Images across the theme were downloaded at their full intrinsic size regardless of how small they render. Worst case: a 1122×1402 source in a 156×156 slot (9×).
I audited every template in headless Chrome at 1440@1x and 390@2x — index, collection (+
campaign/ff-collection/filter-feed), product (+ff/papan-bunga), cart, search, blog, article, page (+ 5 variants) and 404 — comparing each image's intrinsic size against its measured rendered box. Templates with no… -
fix
#87
stop literal Liquid samples from breaking the upload
snippets/changelog-entries.liquidhas never reached any theme. Shopify rejects the whole file on upload, so/changelogis broken on the Horizon theme right now, andshopify theme devrefuses to serve *any* page until it is fixed.``
Liquid syntax error (line 246): Variable '{{ request.origin | append: '/search?q={search_term_string}' was not properly terminated with regexp: /\}\}/``Two changelog entries quote Liquid source as prose, and Liquid parses it rather than…
-
fix
#86
give ARIA labels a role that permits them, and size the policy trigger
Empat kegagalan accessibility dari audit Lighthouse theme ini (2026-07-31, 6 laporan: home/collection/product × mobile/desktop). Semuanya kasus di mana markup-nya sudah berniat benar tapi atributnya dibuang browser.
-
fix
#85
match <ul> tags that carry attributes
Block Description point menampilkan "Deskripsi produk tidak memiliki format list (ul)" pada produk yang jelas punya bullet list.
-
perf
#84
drop Sentry tracing bundle and keep the hero video off the LCP path
Dua perbaikan performa dari hasil Lighthouse 2026-07-31 (6 laporan: home/collection/product x mobile/desktop — mobile 32–33, desktop 48–64). Keduanya biaya yang kita timbulkan sendiri dan bisa diperbaiki di theme.
-
feat
#83
send submissions to the dashboard and Google Chat
[sections/corporate-partner-lead-form.liquid](sections/corporate-partner-lead-form.liquid)
Sebelumnya submit form lead korporat hanya membuka WhatsApp — datanya tidak tersimpan di mana pun dan tidak ada notifikasi, jadi pengunjung yang menutup tab WhatsApp tidak meninggalkan jejak apa pun. Sekarang form ikut mengirim ke endpoint yang sama dengan form registrasi CP Contact:
POST /api/companies?source=web→ baris masuk ke sheetCompany, dansource=webmembuat web-raku… -
feat
#82
make every form field switchable in the editor
[sections/corporate-partner-contact.liquid](sections/corporate-partner-contact.liquid) + [assets/corporate-partner-base.css](assets/corporate-partner-base.css)
Form registrasi korporat sebelumnya terkunci di 18 field. Sekarang tiap field punya toggle sendiri di theme editor:
- Header schema baru "Fields" dengan 18 checkbox, urutannya mengikuti urutan field di form, semua default
true. - Field yang dimatikan dihapus dari DOM, bukan disembunyikan lewat CSS. Ini yang penting:…
- Header schema baru "Fields" dengan 18 checkbox, urutannya mengikuti urutan field di form, semua default
-
feat
#81
let each logo block use an uploaded image
[sections/corporate-partner-proof.liquid](sections/corporate-partner-proof.liquid) — block "Logo / Wordmark" sekarang bisa pakai logo asli, bukan cuma teks.
- Setting baru per block:
Logo(image picker) danLogo max height(12–96px, default 28px) — pola yang sama dengan blockcompanydi CP Trusted Companies. - Kalau logo diisi → yang dirender gambar, teks wordmark/chip diabaikan sepenuhnya (sesuai permintaan: "text nya akan berganti ke image yang di upload").
- Field
Texttetap berg…
- Setting baru per block:
-
feat
#80
add vendor hero as the first page section
Port blok
HEROdari desain corporate landing (corporate-partnership.html) menjadi satu section mandiri, lalu dipasang dipage.corporate-partnersebagai section pertama, di atas herogiftingyang sekarang.sections/corporate-partner-hero-vendor.liquid(baru) — nama di editor CP Hero Vendor (nama "CP Hero" sudah dipakai section hero lama berbahasa Inggris yang statusnyadisableddi page ini; section itu tidak disentuh).- Badge, H1 dengan bagian
<em>terpisah (heading+…
-
feat
#79
give each Occasion block its own product filter and card list
Occasion di block Card kembali jadi child block, bukan dari menu linklist lagi. Setting
Occasion menu(link_list) dan loop-nya dihapus dariblocks/card.liquid.Linklist (#74) memang bikin occasion admin-editable, tapi cuma bisa menentukan *chip mana, urutan apa*. Kartu tiap occasion selalu diambil dari satu aturan hardcoded: produk di koleksi
greeting-card-allyang ber-tagoccasions-<name>. Tidak ada cara memilih kartu per occasion, dan tidak ada cara menampilkan set… -
fix
#78
size the hero by its media, capped at the screen
Hero homepage-nya sekarang mengambil tinggi dari media yang di-upload, bukan dari setting Height, dengan cap setinggi layar.
-
fix
#77
show mobile hero video at source aspect ratio
Video di section Hero homepage terlihat ter-zoom di layar mobile, walaupun source video sudah 4:5.
-
fix
#76
single "Custom..." prefix, and stop wrapping content_for in a condition
Two fixes. The second is the answer to "the Occasion menu setting still isn't there".
-
fix
#75
strip emoji from occasion names and surface the Occasion menu setting
Two follow-ups to #74.
-
feat
#74
drive occasion chips and message prefixes from a link list
The Card block's occasions were child blocks — one
_occasionblock per occasion, each with a hand-typed name. Adding or reordering an occasion meant editing the block tree on every product template. And the PREFIX dropdown was a hardcoded list of three, shared by every occasion, so a Sympathy card and a Birthday card offered the same openings.Occasions now come from a link list, the same way the raku-app
personalized-obextension already does it, and each menu item's… -
revert
#73
Additional information block dynamic name
Membatalkan task 4 dari PR #72 (yang sudah ter-merge). Nama schema block "Additional information" dikembalikan ke nilai statis
"Additional information", menghapus dynamic name"{{ filter_values }}".Hanya menyentuh
blocks/ai_gen_block_b3e6234.liquid(1 baris). Perubahan lain dari #72 (harga Estimated total 20px, z-index drawer, Card media-gate) tidak terpengaruh. -
feat
#72
card media gate + cart/drawer/block-name tweaks
Empat perubahan kecil sesuai permintaan:
- Harga Estimated total (cart) —
.cart-totals__total-valuedisnippets/cart-summary.liquidsekarangfont-size: 20px(desktop & mobile), sebelumnya pakai token--cart-font-size--xl/--font-size--lg. - Drawer filter di collection page —
.filter-collection__drawer(sections/filter-collection.liquid)z-indexnaik dari999→99999supaya di atas overlay lain. - Block "Card" (product page) — Foto & Video — panel step 4 (Enhance Your Message /…
- Harga Estimated total (cart) —
-
fix
#71
drop the placeholder collection cards from the budget menu
The "Shop by Budget" panel rendered two collection cards that were pure placeholder graphics. Its links point at price-bracket collections ("< 300.000", "300.000 - 500.000", …) and none of them carry an image, so every card fell back to the same stock illustration — identical tiles that told a shopper nothing, took the full width of the panel, and squeezed the bracket list beside them.
That menu is now text-only, using the same downgrade (
menu_content_type = 'text') the… -
feat
#70
hide zero-priced products and cover the remaining lists
Builds on 3584981, which introduced
product-hiddenfor[US]andShipping Costutility SKUs and applied it to seven listing loops.1. Third rule — a product priced 0.
``
liquid elsif product.price == 0 assign hidden = true``product.priceis the cheapest variant, which is the figure the card shows, so this matches what a shopper would have seen. Confirmed against the live catalogue: several "Extra Shipping Cost" entries sit at 0.2. Coverage extended from 7 to 13 loops. These…
-
perf
#69
load the reviews widget on product pages only
The Stamped widget was loaded unconditionally from the layout, so every page paid ~51KB for it — cart, search, blog, 404 included — and threw
Uncaught TypeError: e is not a functionon startup where it had nothing to attach to.It is now gated on
template.name == 'product', which covers every product template includingproduct.ff.json. -
fix
#68
resolve the homepage accessibility audit failures
A Lighthouse run of the homepage scored accessibility 83, with four axe rules failing across 51 elements. This fixes all four.
| Rule | Elements | Impact | File | | --- | --- | --- | --- | |
aria-hidden-focus| 30 | serious |assets/slideshow.js| |link-name| 9 | serious |snippets/group.liquid| |list| 3 | serious |blocks/_header-menu.liquid| |target-size| 8 | — |snippets/collection-card.liquid| -
perf
#67
download one video rendition instead of the largest
A Lighthouse audit of the homepage showed the hero video as the single biggest payload on the page: 4.4MB, 44% of the 10.8MB total.
The poster-first facade (#64) already deferred the sources, but on hydration every
<source>was given asrc, and a plain<video>takes the first playable one in document order. Shopify lists its MP4 ladder largest first, so the 1080p file was downloaded for every viewport regardless of how small the box actually was.Deferring the sources is what…
-
fix
#66
remove black flash between the video cover and playback
Reported via Jam: a black flash appears in the hero during the cover → video transition ("Transisi antara cover dan video ada jeda space hitam").
-
feat
#65
add desktop + mobile cover uploads across all video surfaces
Every section/block with an uploaded-video setting now exposes "Video cover (desktop)" and "Video cover (mobile)" image pickers. The cover is the poster painted before the video, with an art-directed mobile crop when set. Covers fall back to the video's preview frame, so existing content renders unchanged.
Follows the hero poster-first work (#64) and extends the same idea — merchant-controlled covers — to the rest of the theme's video surfaces.
-
perf
#64
render hero videos with a poster-first facade
A Lighthouse audit of the homepage (mobile) scored Performance 27, with LCP 12.7s and Speed Index 15.3s. The single largest payload was a 4.4 MB hero video loaded on the critical path.
Root cause in
sections/hero.liquid: hero videos rendered with Shopify's nativevideo_tag, which downloads the full video immediately. And withcustom_mobile_mediaoff, the mobile fallback reused the desktop video at full size — so mobile downloaded the 4.4 MB clip too. -
fix
#63
stop a literal brace from making the whole section fail to upload
sections/header.liquidtidak pernah sampai ke theme mana pun sejakaf65082(PR #49, 17 Juli 2026). Shopify menolak file-nya saat upload:``
Liquid syntax error (line 315): Variable '{{ request.origin | append: '/search?q={search_term_string}' was not properly terminated with regexp: /\}\}/`` -
style
#62
enlarge the line item title and line total on the cart page
Perbesar dua angka di cart page: judul line item dan harga line item yang sudah dikalikan quantity.
| Elemen | Sebelum | Sesudah | | --- | --- | --- | |
.cart-items__title(judul produk) |--font-size--md(1rem) |--font-size--lg(1.125rem) | |.cart-items__price(total baris = harga satuan x qty) |--font-size--sm(0.875rem) |--font-size--md(1rem) | -
feat
#61
add a Custom body size to the typography presets
Adds a Custom option to *Body preset* on the collection description section. Choosing it reveals a px size field, default 14px.
-
fix
#60
cover the full section width and defeat pasted text colours
Fixes the SEO copy below the footer on collection pages: a white strip down the left of the section, and body text that was almost invisible against the dark scheme. Two unrelated causes.
-
style
#59
square off the pagination corners
Removes the rounded corners from pagination — normal, active and hover states all square.
-
refactor
#58
move the Money Bouquet denom into the variant picker
Follow-up to #57. The denomination stepper shipped as its own block that had to be placed by hand — so after #57 merged, nothing appeared on the storefront until someone added it.
It belongs with the variant picker: the sheet count comes from the selected variant, so the two are one control. A separate block could be positioned away from the picker, or left out entirely.
-
feat
#57
add Money Bouquet denomination block
Adds a Money Bouquet denomination stepper: on products tagged
Money Bouquet, the shopper splits the selected variant's lembar (sheet) count between Rp 50.000 and Rp 100.000 notes, with a running total and the payment-transfer note.Brings the workflow from raku-app's
personalize-bouquetblock to Horizon, matching the snippet of the same name shipping inweb-outerbloom-shopify. -
style
#56
square corners on bordered elements (remove border-radius)
Menghilangkan sudut membulat pada elemen ber-border: setiap rule CSS yang mendeklarasikan border terlihat (style
solid/dashed/dll) kiniborder-radius-nya0.Elemen lingkaran/pil/spinner (
50%,999px,9999px,inherit,calc()) dipertahankan agar spinner & tombol bulat tetap normal. -
feat
#55
discount percentage & stacked layout on product page
Menyeragamkan tampilan harga di product page agar sama seperti product card / product list:
- Compare price di atas, dengan persentase diskon
(xx%)di sebelahnya - Regular (sale) price di bawahnya, tersusun vertikal (stacked)
Sebelumnya product page memakai layout inline tanpa persentase diskon, sedangkan product card sudah stacked + persentase.
- Compare price di atas, dengan persentase diskon
-
style
#54
use regular weight for compare price & discount percentage
Memastikan compare price (harga coret) dan persentase diskon selalu tampil dengan bobot regular (tidak bold).
Sebelumnya kedua elemen tidak menyetel
font-weightsendiri, sehingga mewarisi bobot bold dari harga/heading induknya. Sekarang keduanya dipin ke bobot body regular theme (--font-paragraph--weight). -
fix
#53
rework the mobile footer grid and tighten its gap
Reworks the mobile (<750px) footer layout in
sections/footer.liquidand drops the mobile gap to 10px.Before, the two-column mobile footer put INFO OUTERBLOOM and BANTUAN stacked in column 1 and gave the entire B2B group column 2 (spanning both rows). Now:
``
INFO OUTERBLOOM | BANTUAN B2B PROGRAMS | LAYANAN CUSTOMERS JOIN OUR EMAIL LIST (full width)``How it works:
- Standalone blocks (the two menus) are placed by grid auto-flow, one per column.
- A group block is a single grid…
-
fix
#52
keep mega menu open when moving within the active item
Hovering a header nav item sometimes failed to show the mega menu — the panel flickered open then immediately closed, and it was especially reproducible when approaching a menu item from below.
Reported via Jam:
- https://jam.dev/c/c7d7c470-f438-4ed8-a8e0-3d2aca754e12 ("kadang-kadang tidak keluar")
- https://jam.dev/c/ff82d8d5-d5fc-4944-bd12-e5cfb58412d1 ("tidak muncul apabila cursor dari bawah")
-
feat
#51
Card & Message block with Occasion child block
Adds the native Card & Message theme block (
blocks/card.liquid) to the product page — a single-step personalization flow ported from the raku-apppersonalize-bouquetapp-embed, plus its Occasion child block, supporting snippets, and behaviour JS. All files are new (nothing pre-existed onmain). -
feat
#50
collection, search & 404 title rules
Follow-up to #49. Extends the
<title>logic insnippets/meta-tags.liquidto collection, search and 404 pages, replicating the live store's buy-intent copy while fixing bugs the live theme's version carried. Only the<title>block changes; product and every other page type render identically. -
feat
#49
meta description fallback + BreadcrumbList schema
Two additive SEO gaps found while auditing the theme's SEO surface. Both are new signals for Google — nothing existing is removed.
-
docs
#48
stop before committing unless asked
Adds a "stop before committing" rule to this repo's agent guide.
Agents have been finishing a task by committing, pushing and opening a PR unasked. That removes the one cheap checkpoint the reviewer has: reading the diff while it is still a working-tree change. It matters most on the theme repos, where a merge to
maindeploys straight to the live storefront — an unreviewed commit is effectively an unreviewed deploy.The rule:
> Finish the edits, run the verification, report…
-
feat
#47
quantity in the line unit price + scroll to top after removing an item
Two adjustments to the cart page.
-
feat
#46
typography preset settings
The Collection description section (the SEO copy rendered below the footer on collection pages) could only override heading sizes in px. There was no way to point it at one of the theme's typography presets, so its body copy always inherited the global type and headings could be resized but not restyled.
Adds a Typography group to the section schema with two selects:
| Setting | Applies to | Options | | --- | --- | --- | | Body preset |
p,li,blockquote,dd,dt,… -
perf
#45
load components on demand instead of on every page
snippets/scripts.liquidloaded 27 JS modules unconditionally on every template — ~250KB raw / ~58KB gzip. Product-only components (product-form,variant-picker,media-gallery,volume-pricing,price-per-item,quantity-selector,product-inventory,product-sku, …) were downloaded and parsed on the homepage, blog, search and policy pages, andslideshow.js+layered-slideshow.js(46KB together) shipped everywhere whether or not a slideshow existed.Every one of those modules does…
-
feat
#44
image upload, per-section padding, sticky offer, width & corner-radius controls
Merchant-facing controls for the corporate-partner page, added iteratively. All new settings default to the current appearance — nothing changes on the live page until a merchant edits a value. Touches
sections/corporate-partner-*.liquidonly; no editor-owned JSON, routes, or shared assets. -
feat
#43
collection description below footer + cart, footer, filter, pagination, drawer UI refinements
Six requested storefront UI refinements, one commit per concern.
-
feat
moment
recipient share experience — share card, brand type, HEIC
The Moment recipient experience (moment.outerbloom.com — shown when the recipient scans the flower card) shipped several updates: a shareable Moment card (share your photo + the sender’s words as a portrait image to Instagram, X and Threads, Spotify-Wrapped style); Save as an image (fixed on mobile); on-brand design with a bigger photo and Outerbloom’s Source Sans 3 type; iPhone HEIC photos; an IG/TikTok-style mute icon; and no clipping of long recipient names.
-
feat
#42
add global Hover style setting for text links
Adds a global Hover style theme setting so link hover feedback is controlled in one place instead of hardcoded per section.
New Hover settings group (
config/settings_schema.json):global_hover_style: Default / Underline / Underline (animated) / Change color / Bold / Thinglobal_hover_color: color used by *Change color* and the animated underline (falls back to the link's current color)
A body class
global-hover-style-<value>(layout/theme.liquid) drives CSS in… -
feat
#41
add 7 vendor-focused page sections
Adds Why Vendor, Role Segmentation, Product Scope, Coverage Map, Proof, Offer, and Lead Form as self-contained sections ported from the corporate landing design, and wires them into the page.corporate-partner template after enterprise_procurement.
Each section follows the existing corporate-partner-* convention: settings- driven CSS variables, editable blocks, and presets prefilled with the source copy. Bespoke flower/map SVGs are preserved (symbol + gradient ids namespaced…
-
feat
#40
instrument the web-raku order-flow dependency
Storefront bergantung pada web-raku (
app.asmaraku.com) di dua titik kritis untuk konversi, dan keduanya gagal tanpa terlihat:- Checkout.
routes.checkout_create_urldi-POST setiap klik "Check out" (assets/component-cart-items.js). web-raku mengembalikan Storefront checkout URL dengan alamat penerima sudah terisi. Ini bukan plumbing opsional — Shopify mematikan mutationcheckoutCreatepada 2025-04-01 dan Checkout Extensibility membuang param…
- Checkout.
-
docs
#39
track CLAUDE.md + require pre-push sync with origin/main
CLAUDE.md(agent working guide for this theme repo) was untracked, so its workflow rules weren't actually part of the repo and didn't reliably reach agents/Codex. This commits it and tightens one rule. -
feat
#38
full-width heading + dedicated description section
Collection-page template layout work, in two commits.
1. Collection heading & menu list
blocks/image.liquid: on the storefront, an image block with no image (e.g. a collection missing themain_bannermetafield) now renders nothing, so the siblingfillgroup (the title/subtitle) expands to full width instead of leaving a gap for the placeholder. The onboarding placeholder still renders in the theme editor.sections/collection-menu-list.liquid: hide the section on the…
-
feat
#37
set checkout_create_url so delivery prefill actually runs
Sets
Theme.routes.checkout_create_urlinsnippets/scripts.liquidto the web-raku delivery-prefill endpoint, so the cart's custom checkout flow (address prefill + the recipient picker from #36) actually runs. -
feat
#36
recipient picker for multi-recipient checkout prefill
When a cart holds gifts for more than one recipient, clicking Check out now opens a modal to choose which recipient's address pre-fills the Shopify checkout, and sends that choice to the delivery-prefill create service.
A single Shopify checkout carries one shipping address. A gift cart groups each recipient's lines by a shared
idline property, with the address on that group'stype: 'product'line (Alamat Penerima). Previously the service just used the first product… -
feat
#35
render collection descriptions + product-title SEO rule (P0 migration blockers)
Closes the two P0 migration blockers from the SEO/GEO audit.
- Collection SEO copy + internal links — Horizon rendered
collection.descriptionnowhere, dropping hand-written SEO copy and the internal backlinks embedded in it across ~1,098 collections. Newsnippets/collection-seo-description.liquid, rendered frommain-collection.liquidafter<results-list>(so the custom filter's show/hide never affects it). Because the internal links are inline<a>tags inside the…
- Collection SEO copy + internal links — Horizon rendered
-
feat
#34
"Board Details" for Papan Bunga / Standing Flower items
Cart line items whose product type is
Papan BungaorStanding Flowernow show a Board Details box, built from their line-item properties — the board equivalent of the existing "Greeting Card Detail" box.| Label | Line-item property | | --- | --- | | Recipient |
Papan Nama Penerima| | Message |Papan Ucapan| | Sender |Papan Nama Pengirim| | Logo |Papan Logo— rendered as an<img>only when the value containshttps://| -
feat
#33
hide "[US]" and "Shipping Cost" products from listings
Products whose title contains
[US]orShipping Costare utility/helper SKUs (e.g. shipping-fee add-ons). They must stay reachable by direct link, but should not appear when customers browse. This hides them from every storefront product list. -
ci
#32
auto-apply settings-change label on editor-owned JSON PRs
PR Guard's settings-guard check fails whenever a PR touches theme-editor-owned files (
config/settings_data.json,templates/*.json) until someone manually adds thesettings-changelabel — the exact toil we just hit on #31. This adds a companion workflow that applies the label automatically. -
feat
#31
real "You may also like" product recommendations
The cart page's "You may also like" was a *static* Featured collection (a
product-listsection hard-pointed at theallcollection). This makes it the same dynamicproduct-recommendationssection the product page uses — matching both its style and its settings — so cart shoppers see genuine related products instead of a fixed collection. -
feat
#30
product review ratings + WebSite/SearchAction + Organization fixes
Fills the biggest SEO/AEO gaps found in the review (structured data was carrying no review rating despite 4.9★/774 reviews being shown).
- Product: hand-author the Product JSON-LD so it includes
aggregateRatingfrom thereviews.rating/reviews.rating_countmetafields, plusAggregateOffer(low/high price, currency, availability), brand, sku, images. Emitted only when a real rating + non-zero count exist (no fake stars). → review stars in Google + citeable by AI answer engines.
- Product: hand-author the Product JSON-LD so it includes
-
perf
#29
stop feed pagination early on the first empty batch
Follow-up to #27/#28.
products_countcan exceed storefront-visible products (drafts/hidden), so trailing 50-per-page feed pages return empty. Verified: a collection withproducts_count961 has 544 visible products = 11 data pages + 9 empty. Break on the first all-empty batch to skip the wasted requests. Products paginate in order, so this can't drop mid-collection items. Theme Check clean; JS syntax checked; parity with the old products.json path confirmed (544 = 544). -
fix
#28
drop Accept header that made the Option B feed return zero products
Follow-up to #27. The feed fetch sent
Accept: application/json, which makes Shopify serve its built-in collection JSON API ({"collection":...}) instead of the?view=filter-feedtemplate — so the client parsed a non-array and rendered zero products.Removing the header fixes it. Verified on the preview theme:
?view=filter-feedreturns the correct product array.- Prices match
products.jsonexactly (385000 / 800000 compare-at, etc.) — thedivided_by: 100assumption is…
-
perf
#27
replace ~5MB products.json fetch with a compact server feed (Option B)
Implements Option B: keeps the custom filter drawer, but swaps its data source from
/products.json(~5 MB on a large collection) to a compact server-rendered JSON view.- New
templates/collection.filter-feed.liquid(?view=filter-feed) emits only the fields the filter reads — nobody_html— paginated by 50. Transfer drops to ~150–250 KB. fetchAllProductsnow pulls the feed with bounded concurrency (6) and one-retry-then-throw per page (fixes a latent bug where a failed…
- New
-
docs
#26
add an Open Items roadmap section
- Adds a hand-maintained Open items section to the
/changelogpage, below the auto-generated ledger. - Grouped by area (Performance, Reliability & cart, Security & backend, Content & merchant), each item carries a status tag (in progress / planned / needs review / external / merchant).
- Gated by a
show_open_itemssetting (default on) with an editable heading. CSS-only, no JS.
- Adds a hand-maintained Open items section to the
-
perf
#25
harden the collection filter data layer (Option B)
Keeps the custom filter UI and fixes its correctness, safety, and staleness. This is Option B — it does not remove the full-collection
products.jsonfetch (~5 MB on a large collection); that is inherent to client-side filtering and needs native facets (Option A) or a server-rendered dataset.- Stale price/inventory: cache now has a 10-minute TTL (key
v1→v2), so a price/availability change that doesn't change the product count self-heals instead of serving stale data all session.
- Stale price/inventory: cache now has a 10-minute TTL (key
-
fix
#24
show video poster so the homepage hero is never blank
- Fixes a blank homepage hero (major above-the-fold conversion issue): the hero background video rendered with
poster: nil, so withpreload="metadata"and no poster the first ~665px stayed white until the video buffered — and mobile browsers routinely defer autoplay video, so it could stay blank entirely. - Sets
posterto each video's ownpreview_imagefor all four hero slots (desktop/mobile × slot 1/2), so the cover frame shows instantly.
- Fixes a blank homepage hero (major above-the-fold conversion issue): the hero background video rendered with
-
chore
#23
image dimensions on personalization blocks; SHA-pin GitHub Actions
- Adds
width/heightto settings-backed personalization images (greeting-card default image, board preview images) to reduce layout shift (CLS). - Pins
actions/checkoutandactions/github-scriptto full commit SHAs so CI can't silently pull a re-tagged action.
- Adds
-
fix
#22
never fall through to /undefined; guarantee native Shopify checkout fallback
- Fixes the P0 where clicking Check out navigated to
/undefined(404) or threwTypeError: Cannot read properties of undefined (reading 'domain'). - Native Shopify checkout is now the guaranteed fallback for every missing-service, failure, or unsafe-URL case, while the custom delivery-prefill flow is preserved when the app supplies its endpoint.
- Escapes customer greeting-card properties (recipient, sender, message, e-card link) rendered in the cart, closing a permalink/stored XSS.
- Fixes the P0 where clicking Check out navigated to
-
perf
#21
fetch filter pages in parallel and cache them
fetchAllProductsinsections/filter-collection.liquidhadawaitinside aforloop, so it fetched every page ofproducts.jsonone after another even though the pages are independent.Measured against the live store (fast desktop connection, so mobile is worse — each sequential page adds a full round-trip of latency):
| Collection | Products | Pages | Before | After | | |---|---|---|---|---|---| |
all| 4,012 | 17 | 5.48s | 0.47s | 11.7× | |papan-bunga| 1,321 | 6 |… -
feat
#20
rebuild the page as a scannable ledger
The Changelog is an internal page read to answer *"what shipped, when, by whom"*, but nothing about it supported scanning: every entry was an identical card, titles carried
feat(article):prefixes, and the theme's 72px<h1>pushed the list below the fold.- Rows are now an aligned ledger —
Date / Type / PR / Change / By— sharing fixed columns with a header row. A real<table>was the obvious move, but descriptions are multi-paragraph with bullets and code and would…
- Rows are now an aligned ledger —
-
fix
#18
align grid with page width when all slides fit
- Fixes the Jam report from the review team (jam.dev/c/67bfb2c0, "grid image diratakan dengan page"): in the homepage "Moments that Matters (IG tagged)" section, the 4-image grid stops ~48px short of the right page margin while the heading and the "See more stories" button align with it.
- Root cause: the Horizon carousel always subtracts
--peek-next-slide-size(3rem = 48px) from each slide width so the next slide peeks in — but this section has exactly as many slides (4) as…
-
fix
#19
render PR markdown as HTML instead of literal text
- The Changelog page rendered raw Markdown (
## Ringkasan,- bullet,**bold**) as literal text, and cut entries mid-word. Root cause is inchangelog.yml: it took only the PR body's first paragraph, HTML-escaped it verbatim, and hard-truncated at 400 characters. - Worst case was #15, which rendered as nothing but
## Ringkasan— its real content sat in a later paragraph that the first-paragraph rule discarded entirely. - New
.github/scripts/changelog-markdown.jsconverts the PR body…
- The Changelog page rendered raw Markdown (
-
feat
#17
add mobile banner image upload to Article (legacy)
- Fixes the Jam report from the review team (jam.dev/c/83989377, "Banner mobile tidak ada field uploadnya"): the Banner group on the Article (legacy) section only offers a desktop banner upload, so the wide 1450x240 image renders squished on mobile.
- Adds a Banner image (mobile) image picker to
sections/main-article-legacy.liquid(Banner settings group, right under the desktop Banner image). - The banner now renders as a
<picture>: the mobile image is served on screens narrower…
-
fix
#16
make blog page typography & buttons follow Horizon theme settings
- Fixes the Jam report from the review team (jam.dev/c/2da00587): typography and buttons on the blog page ("Inspirations") must follow the settings already configured in the Horizon theme editor instead of the hardcoded legacy pixel styles.
sections/main-blog-legacy.liquid: page title now uses the theme h5 type preset (was hardcoded 28px/capitalize), card titles the h6 preset (was 22px/1px letter-spacing); "Baca selengkapnya" links are now theme.button-secondary;…
-
#15
Add tag-based collection filter (ported from old theme)
Menambahkan filter collection ke theme Horizon baru, mengikuti cara kerja filter di theme lama (
Horizon AI - Old/sections/filter-collection.liquid) sebagai referensi:- Section baru
sections/filter-collection.liquid— bar label filter horizontal (SORT, DELIVERY TO, TYPE, STYLE, OCCASION, COLOR, PRICE RANGE) + drawer kanan berisi checkbox per grup, badge filter aktif, preset harga cepat, tombol CLEAR ALL / VIEW. - Sumber data sama dengan theme lama: tag produk berprefix…
- Section baru
-
feat
#14
port corporate-partner page template from the old theme
- Adds
templates/page.corporate-partner.jsonplus the 14corporate-partner-*sections it references, copied verbatim from the old theme (Horizon AI - Old) including all initial settings (stats, badge, copy, colors, company logos viashopify://shop_images/..., meeting link, etc.). - The sections are fully self-contained: no snippet/global-setting/translation/form dependencies, so nothing else in the theme is touched.
- Verified on theme dev:
/pages/corporate-partnership(the store…
- Adds
-
feat
#13
match policy pages to the global page template layout
- Shopify generates
/policies/*pages itself — no JSON template or sections exist for them — solayout/theme.liquidnow wrapscontent_for_layout(only whenrequest.page_type == 'policy') in the same layout structure as the global page template: narrow (90rem) container, two-column grid, sticky sidebar. - New
snippets/policy-page-sidebar.liquidrenders the sidebar card with the footer menus (footer/ Info Outerbloom andbantuan), mirroringtemplates/page.json's sidebar,…
- Shopify generates
-
feat
#12
add content width setting, widen default page template
sections/main-page.liquidhardcoded thepage-width-contentclass — a 42rem (~672px) reading width — so every page using the section rendered far narrower than other templates (collection, blog, etc. use--normal-page-width).- Adds a Content width select (Reading / Narrow / Normal / Wide) to the section schema. The default stays
content(current behavior), sopage.career,page.contact, andpage.personalizetemplates are completely unaffected. templates/page.json(the global…
-
feat
#11
restyle blog & article pages to legacy design on OS 2.0
- New
sections/main-blog-legacy.liquidreplicating the old theme's blog page (templates/blog.liquid+sections/Blog.liquidin Horizon AI - Old) as a native OS 2.0 section: - H1 with the legacy 160px primary-color underline accent and RSS feed link; shows the active tag (with
kategori_prefix stripped) when filtering. - Horizontal bordered article cards (image left / content right, 50-50, min-height 300px): date + Indonesian reading-time meta ("X menit membaca", chars/1100), 22px…
- New
-
ci
#10
add PR guard for stale branches and theme-editor settings files
- New
.github/workflows/pr-guard.ymlwith two advisory checks on every PR intomain: - Branch is up to date with main — fails when the PR branch is behind
main, meaning it may be missingUpdate from Shopify...theme-editor sync commits. Rebase or mergeorigin/main, then push again. - Theme-editor settings untouched — fails when the PR diff touches files the Shopify theme editor owns (
config/settings_data.json,templates/*.json), so a stale copy of merchant settings never…
- New
-
docs
#9
backfill entry for career page restyle
- The career page restyle (
templates/page.career.json) reachedmainthrough the Shopify GitHub integration sync commit 3bcf487 instead of a pull request, so the Changelog workflow never fired for it. - Backfill the entry by hand in
CHANGELOG.mdandsnippets/changelog-entries.liquid, matching the auto-generated format and linking to the sync commit (same approach as PR #4 did for #3).
- The career page restyle (
-
feat
#8
add heading font weight & case settings
Footer headings previously rendered with inconsistent weights: menu block headings were forced to the heading font weight by a hardcoded CSS rule, while text block headings followed the h6 preset and were wrapped in <strong>.
- Add "Font weight" and "Case" selects to the footer section schema (Footer > Typography), applied to all headings in the footer
- Neutralize <strong>/<b> inside headings when a weight is selected
- Remove the hardcoded menu heading font-weight override
-
feat
#7
add collection menu list section
- New
sections/collection-menu-list.liquid: renders a linked menu of sub-collections on the collection template. - Link source resolution, in priority order: (1) a menu explicitly chosen in section settings, (2) a linklist whose handle matches the current collection handle, (3) the children of the matching item in a configured parent navigation menu.
- Supports grid and carousel layouts, with placeholder cards in the theme editor when no menu matches so the section stays visible.
- New
-
fix
#6
top-align occasions grid so icons and labels line up
- Change the "Occasions" group in
templates/index.jsonfromvertical_alignment: centertoflex-start. - Items with two-line labels ("Sympathy & Funeral", "Grand Opening") were taller than their siblings, so center alignment pushed the other icons out of line on the wrapped mobile grid (and on desktop). Top-aligning the row keeps all icons on one line with labels starting at the same height.
- Change the "Occasions" group in
-
ci
#5
rely on native GitHub sync, add PR description to changelog
Remove deploy-theme.yml — the whole-theme
shopify theme pushis redundant now that branchmainis connected to the theme via Shopify's native GitHub integration, and running both risks double deploys / clobbering theme-editor changes.In changelog.yml, drop the "Deploy changelog to Shopify" step for the same reason; the changelog commit reaches the theme through native sync. Also enrich each entry with a concise description pulled from the PR body (first paragraph, HTML…
-
#4
Backfill changelog entry for PR #3
Manually adds the changelog entry for #3, which the Changelog workflow missed (no run was created for its
pull_request.closedevent).snippets/changelog-entries.liquid: adds thepr-3entry at the top.CHANGELOG.md: adds the- #3 —line.
🤖 Generated with Claude Code
-
#3
Add full theme deploy workflow
Adds
.github/workflows/deploy-theme.ymlthat pushes the whole theme to the target Shopify theme on every push tomain(and via manual dispatch).- Uses secrets
SHOPIFY_CLI_THEME_TOKEN,SHOPIFY_STORE,SHOPIFY_THEME_ID. --nodeletekeeps theme-only files.- Changelog bot commits use
[skip ci], so they don't trigger a redundant full deploy.
⚠️ A full push overwrites
config/settings_data.jsonandtemplates/*.jsonon the theme with the repo versions. Add them to--ignoreif… - Uses secrets
-
#2
Bump CI actions to Node 24 runtimes
Upgrades workflow actions to clear the Node 20 deprecation warning:
actions/checkout@v4→@v5actions/github-script@v7→@v8
Both now run on Node 24. No behavioral change to the changelog automation.
🤖 Generated with Claude Code
-
#1
Document the changelog automation flow
Adds
docs/changelog.mdexplaining how merged PRs flow into the storefront Changelog page and deploy to Shopify.This PR also serves as an end-to-end test of the changelog automation:
- verifies the
Changelogworkflow inserts an entry for this PR, - verifies the PR number links back to this pull request,
- verifies the deploy step pushes the changelog files to Shopify.
🤖 Generated with Claude Code
- verifies the
-
style
3bcf487
restyle career page to legacy design on Horizon color schemes
Restyles
templates/page.career.jsonto match the legacy career page while keeping Horizon color schemes: newcareer-stylescustom-liquid section with scoped CSS (muted subtitles, equal-height value cards with soft shadow and round icon badges, card-style job accordions with dot bullets and divider footers, dark CTA), value cards switched fromheight: fittofill, and spacing added before the first job category. Landed via the Shopify GitHub sync commit, so it has no… -
init
Initial theme setup — homepage, header, footer, and settings synced from Horizon AI
Open items
Work identified but not yet shipped, tracked here for visibility. Not a commitment or timeline.
Performance
- in progress Collection filter downloads the full catalogue on each filter. A compact server-rendered feed (Option B) cuts the transfer ~95% while keeping the current drawer.
- planned Native facet filtering (Option A) would remove the client fetch entirely and add crawlable filter URLs and a no-JS fallback.
Reliability & cart
- needs review Sticky add-to-cart uses a legacy event model — it can overlap the chat widget and double-fire on a fast tap.
- needs review Required personalisation is not enforced before accelerated (Shop Pay) checkout.
- needs review Cart section-render race and timestamp-based line grouping need hardening.
Security & backend
- external Harden the upload endpoints (auth, rate-limit, private storage) for gift photos and corporate KTP/NPWP documents.
- external Review e-card token security (entropy, expiry, no-index) on the e-card domain.
- external Confirm what provides the custom checkout service on the published theme, and remove any dev-tunnel app embeds before go-live.
Content & merchant
- merchant Trust-badge copy fixes ("Prepared with cared" → "care"; "On- time delivery" spacing) — edit in the theme editor.
- merchant Condolence products default to celebratory composer copy; the default should follow product intent.
- merchant Require an explicit delivery-city selection and show the delivery fee before checkout.