Daily review 2026-07-03 - #68
Draft
NghaReformer wants to merge 2 commits into
Draft
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Daily Review — 2026-07-03
1. Bugs & Issues (18 findings)
BUG-01 · Critical ·
src/routes/[lang]/playgrounds/[slug]/+page.svelte:103{#await}block missing{:catch}— silent failure on playground load error{#await nativeLoader}has{:then}but no{:catch error}. If the dynamic import fails (network error, Vite chunk not found), the user sees a permanently blank panel with no error message.Fix: Add
{:catch err}with an error card usingvar(--error)and$t('error.generic'), andconsole.error(err).BUG-02 · High · 10 files —
--orange,--blue,--rednot incss-generator.ts34 usages of undefined CSS custom properties always fall back to hardcoded hex
css-generator.tsemits--amber,--error,--accent— not--orange,--blue,--red. All 34 references silently use their hex fallbacks, bypassing the theme system.--orange#f59e0b--amber--red#ef4444--error--blue#3b82f6--accentAffected:
KpiStrip.svelte:76,amortization/ScheduleTable.svelte:240,246,259,265,amortization/InputsPanel.svelte:604,605,amortization/ChartPanel.svelte(9 lines),bank-reconciliation/LedgerPanel.svelte:175,bank-reconciliation/VarianceScale.svelte(6 lines),bank-reconciliation/TransactionJournal.svelte,bank-reconciliation/ScenarioWalkthrough.svelte.Fix: Either alias
--orange/--blue/--redto existing tokens incss-generator.ts, or rename all call sites to use the correct tokens (preferred).BUG-03 · High ·
src/lib/playgrounds/bank-reconciliation/components/CategoryBreakdownDonut.svelte:31–399 hardcoded hex colors in a JS object — completely outside the theme system
Passed to
style="background: {seg.color}"(line 120). Not themeable; several duplicate existing tokens.Fix: Read CSS tokens at mount via
getComputedStyle(document.documentElement), or assign CSS classes per category and usevar()/fill: currentColorin scoped styles.BUG-04 · High ·
src/app.html:1<html lang="en">hardcoded — SSR delivers wrong language for French routesThe layout's
$effectfixes this client-side only. During SSR,/fr/*pages are servedlang="en"in raw HTML, misleading screen readers and search indexers.Fix: In
hooks.server.ts, detect locale fromevent.url.pathnamein thehandlehook and inject viaresolve(event, { transformPageChunk: … }).BUG-05 · High ·
src/routes/[lang]/privacy/+page.svelteEntire privacy page is hardcoded English —
/fr/privacyrenders untranslatedimport { t } from '$lib/i18n'is present but never called. All six<h2>headings and body paragraphs are raw English strings.Fix: Create
privacy.en.ts/privacy.fr.tsnamespaces, register them, and replace all hardcoded strings with$t().BUG-06 · High ·
src/lib/components/playground/PlaygroundTabs.svelteTab buttons lose their accessible name on mobile
At ≤480 px,
.tab-label { display: none }hides the text; the icon<span>already hasaria-hidden="true". The<button role="tab">has no accessible name for screen readers on small screens.Fix: Use
.visually-hidden(already inapp.css) instead ofdisplay: none, or addaria-label={$t(tab.labelKey)}to the button.BUG-07 · Medium ·
src/lib/playgrounds/cvp/components/ChartToolbar.svelte:135,141--overlay-coloris not in the theme; its two fallback values conflict--overlay-coloris never defined. The differing fallbacks mean the same variable would render two completely different colors if ever set.Fix: Remove
--overlay-colorand use--text-muted/--accentdirectly in separate rules.BUG-08 · Medium ·
src/lib/components/playground/ExercisePanel.svelte:11–13Difficulty labels hardcoded in French in a shared component
English users see French labels. Keys already exist in the amortization and journal-entry namespaces.
Fix: Promote to
shell.difficulty.*incommon.en.ts/common.fr.tsand replace with$t().BUG-09 · Medium ·
src/lib/components/Nav.svelte:16,28Hardcoded English
aria-labelattributes not i18n'daria-label="Main navigation"andaria-label="Toggle menu"are announced by screen readers and must be translated.Fix: Add
nav.ariaLabel/nav.toggleMenutocommon.en.ts/common.fr.ts.BUG-10 · Medium ·
src/routes/[lang]/chart-of-accounts/+page.svelte:134,153Two ARIA role violations on filter controls
role="tablist"but children have norole="tab"— ownership violation. These are mutually exclusive filters;role="radiogroup"/role="radio"is correct.role="radiogroup"/role="radio"correctly but all four buttons are independently Tab-focusable. ARIA spec requires roving tabindex (only the active radio in tab order; siblings navigated by arrow keys).Fix: Switch chips to
role="radiogroup". Implement roving tabindex on the framework switcher.BUG-11 · Medium ·
src/lib/components/WaitlistForm.svelte:55Inline style on honeypot
<div>violates no-inline-styles ruleTechnique is valid; the rule is not.
Fix: Move to a scoped
.hp-trap { position: absolute; left: -9999px; … }class.BUG-12 · Medium · Amortization + Interest playgrounds
CSS
transition/animation withoutprefers-reduced-motionguardBank-reconciliation consistently wraps animations in
@media (prefers-reduced-motion: reduce). This guard is absent from the interest playground components and the spinner@keyframes spinin[slug]/+page.svelte:303.Fix: Mirror the bank-reconciliation pattern in the affected components.
BUG-13 · Low ·
src/lib/i18n/namespaces/playgrounds.fr.ts:21French translation has grammatical error and semantic mismatch
'pg.journal-entry.desc': 'Calculateur de écritures comptables.'demust contract tod'before a vowel. "Calculateur" also misrepresents an interactive journal.Suggested fix:
"Tenue de journal comptable en partie double avec le plan SYSCOHADA."BUG-14 · Low ·
src/lib/components/playground/NumberField.svelte:100,bank-reconciliation/components/InputsPanel.svelte:60,91--bg-inputand--spacing-mdnot in the theme token setBoth have working CSS fallbacks but cannot be overridden by future theme changes.
Fix: Add to
tokens.ts/css-generator.ts, or remove the primary reference and use the fallback values directly.BUG-15 · Low ·
src/lib/components/feedback/FeedbackLauncher.svelte:12,ScreenshotCapture.svelte:16onMountused in runes-mode components — non-idiomatic Svelte 5Both files use
$state()(runes mode) but callonMount. Functionally correct but$effectis the idiomatic Svelte 5 equivalent.Fix: Replace
onMount(() => { …; return cleanup })with$effect(() => { …; return cleanup }).BUG-16 · Low ·
src/lib/components/playground/PlaygroundSettings.svelte:94Hardcoded accent hex inside SVG data URI
stroke='%237c7fff'decodes to#7c7fff— the current--accenttoken. Will silently drift if the accent changes.Fix: Generate the SVG string at runtime from a JS token reference, or add a comment documenting the coupling with
tokens.ts.BUG-17 · Low ·
src/lib/playgrounds/journal-entry/components/EntryHistory.svelte:400color: #fffhardcoded on.btn-confirmWill fail WCAG AA contrast in any light-mode or high-contrast theme.
Fix: Replace with
color: var(--text-primary).BUG-18 · Low ·
src/lib/playgrounds/bank-reconciliation/components/CategoryBreakdownDonut.svelte:120Dynamic inline style binding serialises hardcoded hex into the DOM
Compounds BUG-03. Resolves when BUG-03 is fixed.
2. New Playground Ideas
Existing native modules: TVM, Amortization, Bank Reconciliation, Journal Entry, Interest Lab
Existing static iframes: CVP, Depreciation
IDEA-01 — Inventory Valuation Methods
EN: Inventory Valuation Methods | FR: Méthodes d'évaluation des stocks
Target: Licence 2 / Licence 3
Pedagogical objective: SYSCOHADA (class 3) uses CMUP (weighted average cost) as the primary method; IFRS also permits FIFO. Students confuse the methods and cannot explain why profit differs between them under inflationary vs deflationary price trends.
Core interaction: Learner enters 3–6 purchase lots (qty, unit cost) and a series of sales. A side-by-side table shows moving CMUP (SYSCOHADA default), periodic CMUP, and FIFO — closing stock value, COGS, and gross margin per method. A live chart shows margin divergence as the price trend changes.
Inputs/Outputs: Purchase lots, sales lots, period toggle → closing stock value, COGS, gross margin per method, journal entries (class 3 ↔ 6).
Why it complements existing playgrounds: Journal Entry covers the mechanics of stock entries; this teaches what value fills those entries — the upstream decision JE assumes as given.
IDEA-02 — Financial Ratio Analyzer
EN: Financial Ratio Analyzer | FR: Analyseur de ratios financiers
Target: Licence 3 / Master 1 / practicing professional
Pedagogical objective: Students recite ratio formulas but cannot interpret a real set of financial statements. This playground builds diagnostic intuition: is a current ratio of 1.2 safe or dangerous? It depends on industry and receivables quality.
Core interaction: Learner enters simplified SYSCOHADA-format balance sheet and income statement line items (classes 1–5). The playground computes 12–15 ratios grouped by category (liquidity, solvency, profitability, activity) and colour-codes each against sector benchmarks (commerce, manufacturing, services, agriculture — OHADA zone norms). A radar chart shows the overall financial profile.
Inputs/Outputs: ~18 financial statement line items, sector selection → ratios with colour-coded assessment, radar chart, narrative diagnostic.
Why it complements existing playgrounds: Downstream from Journal Entry + Bank Reconciliation — the learner sees what all those entries aggregate into.
IDEA-03 — Budget Variance Analyzer
EN: Budget Variance Analyzer | FR: Analyseur d'écarts budgétaires
Target: Licence 3 / Master 1 / management accountant
Pedagogical objective: Students conflate "favourable variance" with "good", which breaks when a favourable volume variance masks a degraded margin mix. Fixes the core misconception in SYSCOHADA compta analytique (section 9).
Core interaction: Learner enters budget and actual figures for volume, price, variable cost/unit, and fixed costs. The playground decomposes total profit variance into four components shown in a waterfall chart. A what-if mode isolates one driver while holding others constant.
Inputs/Outputs: Budget + actual values for 4 drivers → 4 variance components, waterfall chart, favourable/adverse flags, management commentary template.
Why it complements existing playgrounds: Connects to CVP (which sets up the cost structure) and adds the temporal managerial-control dimension CVP's static break-even analysis lacks.
IDEA-04 — Payroll & Social Charges
EN: Payroll & Social Charges | FR: Calcul de la paie et des charges sociales
Target: Licence 2 / practicing bookkeeper / HR administrator
Pedagogical objective: Payroll is the highest-frequency bookkeeping task in OHADA-zone SMEs. Students cannot split gross salary into CNPS employee/employer shares, compute IRPP brackets, or produce the journal entries (accounts 421, 431, 437, 447). Rates vary by country; the playground makes this explicit.
Core interaction: Learner selects country (Cameroon, Côte d'Ivoire, Sénégal, or generic), enters gross salary, benefits in kind, and dependants. The playground computes net salary, employee deductions (CNPS + IRPP), employer charges (CNPS + FDFP), and total wage cost. A journal entry panel shows the salary booking entry and payment entry.
Inputs/Outputs: Country, gross salary, benefits, dependants → deductions itemised, net pay, total labour cost, 2 journal entries, payslip summary.
Why it complements existing playgrounds: Journal Entry covers mechanics; this provides a real, high-frequency context that auto-fills realistic amounts — bridging abstract accounting rules to the monthly HR workflow every OHADA-zone SME performs.
IDEA-05 — Fixed Asset Disposal & Revaluation
EN: Fixed Asset Lifecycle | FR: Cycle de vie d'une immobilisation
Target: Licence 3 / Master 1
Pedagogical objective: Asset disposal is the most frequently mis-journalised event in SYSCOHADA practical exams. Students struggle with three scenarios: at book value (no gain/loss), above (class 77 gain), below (class 67 loss). Revaluation adds a fourth complexity. The existing Depreciation playground stops at end of useful life; this picks up from there.
Core interaction: Learner enters asset cost, acquisition date, depreciation method/rate (compatible with Depreciation playground output), disposal date, and proceeds. The playground computes book value at disposal, gain/loss, and generates the full disposal journal entry. A Revaluation tab applies a price index and shows the resulting equity reserve entry.
Inputs/Outputs: Acquisition cost/date, depreciation method, disposal date + proceeds → cumulative depreciation, book value, gain/loss, disposal journal entry, optional revaluation entry — all using SYSCOHADA class 2/6/7 accounts.
Why it complements existing playgrounds: Closes the lifecycle loop that Depreciation opens. Together they give end-to-end coverage of a fixed asset — the three canonical SYSCOHADA exam questions.