From d5b2b557f344077b366961c9537a1aaa90e0e834 Mon Sep 17 00:00:00 2001 From: Scott Idem Date: Wed, 8 Apr 2026 14:19:48 -0400 Subject: [PATCH] chore(badges): hex-only body_text_color + form color picker; renderer default black --- .../[badge_id]/ae_comp__badge_obj_view.svelte | 25 ++++---------- .../ae_comp__badge_template_form.svelte | 33 ++++++++++++++----- tailwind.config.ts | 9 ++++- 3 files changed, 40 insertions(+), 27 deletions(-) diff --git a/src/routes/events/[event_id]/(badges)/badges/[badge_id]/ae_comp__badge_obj_view.svelte b/src/routes/events/[event_id]/(badges)/badges/[badge_id]/ae_comp__badge_obj_view.svelte index 0789a8eb..3583211e 100644 --- a/src/routes/events/[event_id]/(badges)/badges/[badge_id]/ae_comp__badge_obj_view.svelte +++ b/src/routes/events/[event_id]/(badges)/badges/[badge_id]/ae_comp__badge_obj_view.svelte @@ -296,28 +296,17 @@ let qr_back_justify = $derived.by(() => { }); // Body text color: can be a Tailwind `text-*` class or a hex value like `#112233`. -let body_text_color_class = $derived.by(() => { - const cfg = template_cfg || {}; - const raw = cfg?.body_text_color ?? cfg?.text_color ?? ''; - if (!raw || typeof raw !== 'string') return ''; - const v = raw.trim(); - if (v.startsWith('text-')) return v; - // Map simple color names to Tailwind where reasonable (e.g., 'white' -> 'text-white') - if (/^[a-zA-Z]+$/.test(v)) { - const pick = v.toLowerCase(); - const allowed = ['white','black','gray','red','blue','green','yellow','indigo','purple','pink']; - if (allowed.includes(pick)) return `text-${pick}`; - } - return ''; -}); - +// Prefer an explicit hex fallback stored as `body_text_color_hex` in `cfg_json`. +// The `body_text_color` key may still contain a Tailwind class for styling +// when the compiled CSS includes it; the hex ensures correct rendering when +// classes are purged at build-time. let body_text_color_style = $derived.by(() => { const cfg = template_cfg || {}; const raw = cfg?.body_text_color ?? cfg?.text_color ?? ''; - if (!raw || typeof raw !== 'string') return ''; + if (!raw || typeof raw !== 'string') return 'color: #000000;'; const v = raw.trim(); if (/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(v)) return `color: ${v};`; - return ''; + return 'color: #000000;'; }); /** @@ -686,7 +675,7 @@ const code_to_icon: { items-center justify-end overflow-clip p-0 px-8 pb-1 - {body_text_color_class || 'text-white'} + text-black gap-0 " style="{body_text_color_style}"> diff --git a/src/routes/events/[event_id]/(badges)/templates/ae_comp__badge_template_form.svelte b/src/routes/events/[event_id]/(badges)/templates/ae_comp__badge_template_form.svelte index 404ae276..08a94444 100644 --- a/src/routes/events/[event_id]/(badges)/templates/ae_comp__badge_template_form.svelte +++ b/src/routes/events/[event_id]/(badges)/templates/ae_comp__badge_template_form.svelte @@ -57,8 +57,8 @@ let cfg_show_qr_back = $state(true); let cfg_hide_title = $state(false); let cfg_hide_affiliations = $state(false); let cfg_hide_location = $state(false); -// Body text color: Tailwind class (e.g. 'text-black') or hex (e.g. '#000000') -let cfg_body_text_color = $state('text-black'); +// Body text color (hex) +let cfg_body_text_color = $state('#000000'); // Alignment overrides: 'left' | 'center' | 'right' | 'justify' let cfg_align_name = $state('center'); let cfg_align_title = $state('center'); @@ -128,8 +128,13 @@ async function load_template(id: string) { cfg_hide_affiliations = parsed_cfg.hasOwnProperty('hide_affiliations') ? !!parsed_cfg.hide_affiliations : false; cfg_hide_location = parsed_cfg.hasOwnProperty('hide_location') ? !!parsed_cfg.hide_location : false; - // Body text color - cfg_body_text_color = parsed_cfg.body_text_color ?? parsed_cfg.text_color ?? 'text-white'; + // Body text color (hex-only). Prefer explicit hex keys. + const _hex_candidate = parsed_cfg.body_text_color_hex ?? parsed_cfg.body_text_color ?? parsed_cfg.text_color ?? ''; + if (typeof _hex_candidate === 'string' && /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(_hex_candidate.trim())) { + cfg_body_text_color = _hex_candidate.trim(); + } else { + cfg_body_text_color = '#000000'; + } // Alignment overrides (nested under cfg_json.align and cfg_json.qr_alignment) cfg_align_name = parsed_cfg?.align?.name ?? parsed_cfg.align_name ?? 'center'; @@ -190,8 +195,14 @@ async function handle_submit() { cfg_obj.qr_alignment.front = cfg_qr_alignment_front; cfg_obj.qr_alignment.back = cfg_qr_alignment_back; - // Body text color (Tailwind class or hex) - cfg_obj.body_text_color = cfg_body_text_color; + // Body text color (hex-only) + if (typeof cfg_body_text_color === 'string' && /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(cfg_body_text_color.trim())) { + cfg_obj.body_text_color = cfg_body_text_color.trim(); + } else { + // Remove any stale color keys to avoid keeping invalid values + if ((cfg_obj as any)?.body_text_color) delete (cfg_obj as any).body_text_color; + if ((cfg_obj as any)?.body_text_color_hex) delete (cfg_obj as any).body_text_color_hex; + } const data_to_save: key_val = { name, @@ -432,8 +443,14 @@ function handle_cancel() {

diff --git a/tailwind.config.ts b/tailwind.config.ts index 986f9aa9..23cdfbdd 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -1,4 +1,11 @@ import type { Config } from 'tailwindcss'; + +// Tailwind's shipped `Config` type may not include the `safelist` option +// depending on the installed types/version. Extend it locally so we can +// use `safelist` without TypeScript errors. +type ConfigWithSafelist = Config & { + safelist?: Array; +}; import { skeleton } from '@skeletonlabs/skeleton'; import forms from '@tailwindcss/forms'; import typography from '@tailwindcss/typography'; @@ -40,6 +47,6 @@ const config = { } }) ] -} satisfies Config; +} satisfies ConfigWithSafelist; export default config;