PAGE_CSS carried an unconditional `@media (prefers-color-scheme: dark)`
block that redefined --bg, --surface, --text and --border. The merchant
theme override only sets accent, button and typography tokens, so a
visitor whose OS is in dark mode saw a dark card with the merchant's
button colour on it, and no setting could change that. The form is
rendered in a modal on top of the shop theme, which the app cannot read
and which is almost always light.
Move the dark palette out of PAGE_CSS into DARK_VARS and let renderShell
apply it according to a new "themeScheme" setting: light (default), dark,
or auto (follows prefers-color-scheme, the previous behaviour).
The cascade is now base light palette -> chosen scheme -> merchant
override, so custom accent and button colours win under either scheme.
`color-scheme` and its meta tag follow the same setting, which keeps the
native controls consistent.
Existing rows have themeScheme NULL and fall back to the light default.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mv83a29B4eFv5ixoj6PoE1
Il form era gia' tokenizzato (~30 var CSS in :root): non riscriviamo nulla,
iniettiamo un blocco di override DOPO il CSS di base, cosi' un errore di
configurazione del merchant non puo' rompere il default.
- theme.ts (modulo PURO, condiviso con la futura anteprima admin): ThemeTokens,
FONT_PRESETS, themeStyle(). Sanificazione a monte: colori solo esadecimali,
raggio e larghezza clampati, font da whitelist, CSS custom con rimozione di
</style, @import, expression(), javascript: e cap a 4000 char.
Derivati: --focus-ring da accent (rgba), --primary-bg-hover per shading.
- recesso.server: tokenizzati anche font (--font) e larghezza (--card-max), che
erano hardcoded; renderShell(inner, theme) inietta l'override; renderStep1..4
accettano e propagano il tema.
- Settings: themeAccent/ButtonBg/ButtonText/Radius/Font/Width/CustomCss + migrazione.
- proxy: loadTheme(shop) in loader e action, propagato ai 22 punti di render.
Nell'early-return senza sessione il tema e' null (non conosciamo lo shop).
fly.toml: min_machines_running resta 0 per scelta (TODO go-live, vedi
PROTECTED-CUSTOMER-DATA.md §5.2).
- opTextUnfulfilled / opTextShipped editabili dal merchant, con segnaposto
{{returnAddress}} {{returnCost}} {{orderState}} {{customerName}} {{orderName}}
{{shopName}}. Default = testi attuali. returnCost deriva da returnAtCustomerExpense
(a tuo/nostro carico, Art. 57). returnInstructions deprecato (assorbito da opTextShipped).
- Pagina Impostazioni riorganizzata in 4 tab (Email / Notifiche / Regole recesso /
Reso e stato ordine) + anteprima live del riquadro per stato (non evaso e spedito).
- emailTemplate: renderOperationalBlock ora usa i template + sostituzione segnaposto;
renderOperationalPreview esportato per l'admin. Migrazione op_texts (additiva).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mv83a29B4eFv5ixoj6PoE1
Comportamenti Pizeta-confirmed, tutti toggle per-shop:
- stateAwareEmail (default ON): la ricevuta durevole include un blocco operativo
per stato - non evaso => annullo+rimborso; spedito/consegnato => istruzioni reso
(indirizzo, spese a carico Art.57, prodotto integro, rimborso dopo rientro).
- autoCancelUnfulfilled (default OFF): recesso su ordine non evaso => orderCancel
(refund+restock). Abilita anche lo stop del remarketing via orders/cancelled.
- returnAtCustomerExpense / returnInstructions / returnAddress: config istruzioni reso.
Impl: helper orderState + cancelOrder (recesso.server); renderOperationalBlock +
param operational in renderReceiptHtml (emailTemplate); mailer passa operational;
proxy calcola stato, passa alla ricevuta, auto-annulla; sezione admin 'Reso e stato
ordine'. Migrazione a6bis_state_ops.
+ CHECKLIST-COMPLIANCE-MERCHANT.md: cosa fa l'app vs obblighi del merchant.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mv83a29B4eFv5ixoj6PoE1
Storefront (theme app extension recesso-storefront):
- App block + app embed che linkano a /apps/recesso; modal via iframe con
fallback progressive-enhancement; apertura immediata, dimensione fissa
- Redesign form: pattern modal moderno (header/footer fissi, corpo scorrevole),
modalita' embed, popover info "i", testo ridotto, de-AI, trattini al posto degli em-dash
Ricevuta email:
- Dati per-shop dinamici da Shopify (nome, url, link stato ordine) via getShopInfo + statusPageUrl
- Template editabili VINCOLATI (oggetto/introduzione/nota) con segnaposto; layout
fisso e conforme (dichiarazione, timestamp, avviso art. 54-bis)
- Pagina admin (Polaris) con anteprima live + ripristina default
- Campi email in Settings (Prisma) + migrazioni
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mv83a29B4eFv5ixoj6PoE1