Skip to content

feat!: calm design system, rebuilt tools, shareable links#54

Merged
Arlind-dev merged 46 commits into
mainfrom
next
Jul 1, 2026
Merged

feat!: calm design system, rebuilt tools, shareable links#54
Arlind-dev merged 46 commits into
mainfrom
next

Conversation

@Arlind-dev

@Arlind-dev Arlind-dev commented Jun 29, 2026

Copy link
Copy Markdown
Owner

Reworks the app end to end: a calm, consistent design system, rebuilt tools, and shareable links — same Swiss grading logic, tests, and Catppuccin theming.

Design system & shared components

  • Catppuccin token layer in app.css (Latte/Mocha): hairline borders, one radius/weight scale, no neon glow.
  • Shared primitives replace per-page duplication: Page, Button, ClearButton, ResultBar, StatusChip, NumberField, GradeRow, RoundingSelect, ShareButton, ThemeToggle, LocaleSelect, ShortcutHint, NavBar, Footer.
  • Dropped the daisyUI and flowbite-svelte-icons dependencies.

Tools

  • Calculator — points → grade, unchanged formula.
  • Average — weighted average with nested sub-grades and drag-to-reorder; grade/weight edits now commit on blur.
  • Required grade — simplified to a target average plus a remaining-exams count (was a per-exam table repeating one figure).
  • Final grade (QV) — rebuilt apprenticeship-qualification tool: per-preset weighted components, tracks, eliminatory fall grades, and pass/fail.

Chrome

  • Minimal centered top bar with theme toggle and locale select; mobile drawer.
  • No theme/locale flash on load; both persist.
  • Footer meta line linking to the repo.

Sharing

  • Native share sheet with clipboard/prompt fallback, plus share URLs guarded against oversized payloads on both encode and decode.

Housekeeping

  • Removed CSV export and dead code (export.ts, focus.ts, unused helpers); LF normalization + .gitattributes.

Verification

  • bun run check — 0 errors, 0 warnings.
  • bun test — 56 pass.
  • bun run build — clean (Cloudflare adapter).

@Arlind-dev Arlind-dev marked this pull request as draft June 29, 2026 17:12
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 29, 2026

Copy link
Copy Markdown

Deploying swiss-grades with  Cloudflare Pages  Cloudflare Pages

Latest commit: 508c393
Status: ✅  Deploy successful!
Preview URL: https://40a5f0cf.swiss-grades.pages.dev
Branch Preview URL: https://next.swiss-grades.pages.dev

View logs

renovate Bot and others added 27 commits July 1, 2026 14:02
Load Inter alongside JetBrains Mono and register both as Tailwind
--font-sans / --font-mono. The body now defaults to Inter for UI text,
while numeric values stay monospace via the font-mono utility (applied
here to the grade and weight inputs in GradeRow).

Claude-Session: https://claude.ai/code/session_013Tk2vvh35kVV1JSkMfQSzu
The large grade result numbers used a fixed text-8xl and overflowed on
phones; they now scale text-6xl -> sm:text-7xl -> lg:text-8xl and keep
the mono font. Page headings step down from text-4xl to text-3xl on
small screens, and numeric inputs/results use the mono font.

Claude-Session: https://claude.ai/code/session_013Tk2vvh35kVV1JSkMfQSzu
…blet

- Final grade scales text-6xl -> sm:text-8xl -> lg:text-9xl (was a fixed
  text-9xl that overflowed on mobile).
- The status badge no longer clips: dropped the fixed sm:w-28 width in
  favour of a full-width, centered, wrapping badge so "Keine Fallnote"
  and the longer FR/IT strings fit.
- The 5-column component grid now activates at lg instead of sm, so
  tablets keep the comfortable stacked-card layout.
- Heading is responsive and grade/weight values use the mono font.

Claude-Session: https://claude.ai/code/session_013Tk2vvh35kVV1JSkMfQSzu
Convert all tracked text files from CRLF (and mixed CRLF/LF) to LF, the
standard on Linux, and add a .gitattributes (`* text=auto eol=lf`) so git
checks out and normalizes text files to LF going forward. This prevents
the phantom whitespace churn that mixed endings caused on edits. No
content changes — line endings only.

Claude-Session: https://claude.ai/code/session_013Tk2vvh35kVV1JSkMfQSzu
The Inter pairing felt too different from the original monospace look;
revert the body font to JetBrains Mono and drop the Inter web-font load.
The --font-mono token and font-mono utilities stay (everything is mono
again, so they're harmless no-ops on already-mono numeric elements).

Claude-Session: https://claude.ai/code/session_013Tk2vvh35kVV1JSkMfQSzu
daisyUI's .badge has a fixed height and overflow clipping, so the longer
"Fall grade < 4.0" status (and its FR/IT equivalents) was cut off when it
wrapped. Add h-auto + min-h-8 so the badge grows to fit wrapped text.

Claude-Session: https://claude.ai/code/session_013Tk2vvh35kVV1JSkMfQSzu
The btn-ghost clear/reset buttons set no text colour, so they fell back to
daisyUI's default and were nearly invisible against the dark (Mocha) base.
Add class:text-ctp-subtext1={!confirmClear} so the resting state follows
the Catppuccin theme; the red confirm state keeps text-ctp-base. Applies
to the QV reset button and the clear buttons on average/calculator/needed.

Claude-Session: https://claude.ai/code/session_013Tk2vvh35kVV1JSkMfQSzu
The icon variable was lowercase (activePresetIcon), so Svelte rendered
<activePresetIcon> as a literal unknown HTML element instead of the
component, leaving the selector button's icon slot empty. The selection
modal worked because it used a capitalised const (ItemIcon). Rename to
ActivePresetIcon so the chosen preset's logo renders on the button too.

Claude-Session: https://claude.ai/code/session_013Tk2vvh35kVV1JSkMfQSzu
Part of the UI rework. Retune the global daisyUI tweaks in app.css to a
calm, consistent baseline (hairline borders, one radius scale, restrained
font weights, soft shadows, a .section-label helper) and add small shared
components to DRY up the pages and keep them consistent:

- Page: standard heading + spacing shell
- ToolbarRow: rounding + actions header
- ClearButton: two-step confirm reset (was duplicated on all 4 pages)
- ResultDisplay: understated inline result + optional pass/fail chip
- StatusChip: one tonal pill for all statuses
- EmptyState: calm placeholder
- NavLink: single nav item (icon + active state), replaces 8x duplication

Calm RoundingSelect/ShareButton to match. No page wiring yet; no logic
changes.

Claude-Session: https://claude.ai/code/session_013Tk2vvh35kVV1JSkMfQSzu
Replace the 8x-duplicated nav active-state markup (desktop bar + mobile
drawer) with a single NavLink component driven by a tools array, each with
an icon (Average=scale, Points=convert, Required=flag, QV=cap) and one
consistent active treatment + focus ring. Labels collapse to icon-only
below md via short i18n labels (navShort) to keep the bar tidy.

Also: align the footer width to the main container (max-w-5xl, was 3xl),
calm the locale dropdown and brand mark, and add shared i18n strings
(navShort, common.pass/fail/emptyState, calculator/average subtitles) to
all four locales.

Claude-Session: https://claude.ai/code/session_013Tk2vvh35kVV1JSkMfQSzu
Wrap in <Page>, use ToolbarRow/ClearButton/ResultDisplay/EmptyState.
Replace the giant glowing result card with an understated inline result
+ pass/fail chip, add an empty state, and render the formula as calm
borderless text. No calculation changes.

Claude-Session: https://claude.ai/code/session_013Tk2vvh35kVV1JSkMfQSzu
Wrap in <Page>, move the rounding/CSV/share into ToolbarRow, replace the
inline clear logic with <ClearButton>, and swap the giant glowing result
card for an understated inline ResultDisplay + pass/fail chip (empty state
when there is no average yet). Delta pills use calm tonal colors. Calm
GradeRow weights/borders to match. Drag-reorder and keyboard shortcuts
unchanged.

Claude-Session: https://claude.ai/code/session_013Tk2vvh35kVV1JSkMfQSzu
…esult

Wrap in <Page>, use ToolbarRow/ClearButton/ResultDisplay/StatusChip/
EmptyState. Future-exam rows now stack cleanly on mobile (name on top,
weight + remove below) instead of a cramped fixed-width flex. Replace the
results table that repeated the same required grade for every exam with a
single clear result (or an impossible/achieved chip), with the assumption
and best-attainable as calm footnotes. Calculation unchanged.

Claude-Session: https://claude.ai/code/session_013Tk2vvh35kVV1JSkMfQSzu
Wrap in <Page>, flatten the cards (hairline borders, soft shadows, one
radius), drop font-black/glow, and replace every fall-grade/status pill
with the shared <StatusChip> (tonal success/error/neutral). The final
grade is now understated (no giant glow) with a calm pass/fail chip, and
the reset uses the shared <ClearButton>. Sticky column header aligned to
the 80px navbar. No QV logic changes.

Claude-Session: https://claude.ai/code/session_013Tk2vvh35kVV1JSkMfQSzu
Rename the nav tabs to concise, clearer words (Average / Calculator /
Target / QV) in all four locales, and add common.clearAll/clearConfirm so
every page can share one consistent clear/reset label.

Claude-Session: https://claude.ai/code/session_013Tk2vvh35kVV1JSkMfQSzu
Remove the per-tool icons from the top bar and drawer (NavLink icon is now
optional) and show the concise renamed labels at all widths. Cleaner, less
"vibecoded" navigation.

Claude-Session: https://claude.ai/code/session_013Tk2vvh35kVV1JSkMfQSzu
Polish round from review feedback:
- New ShortcutHint component with a theme-safe .kbd-key style — the old
  keyboard hint was unreadable in dark mode and too small.
- One consistent clear/reset label everywhere (was "Reset QV" vs "Clear
  all"); unify the Add grade / Add exam button styles.
- Replace the bespoke QV track "join" buttons with a clean segmented
  control matching the rest of the app.
- Remove the per-page subtitles and flatten the QV overview icon chips to a
  single lavender accent (drop the multi-colour rainbow).

Claude-Session: https://claude.ai/code/session_013Tk2vvh35kVV1JSkMfQSzu
The greenfield UI uses a hand-built Catppuccin token layer instead of
daisyUI's theme/component system, and inline SVGs instead of the flowbite
icon set. Removing both deps also drops the vite optimizeDeps workaround.

Claude-Session: https://claude.ai/code/session_013Tk2vvh35kVV1JSkMfQSzu
Hand-built two-theme token system (Latte light leaning white, Mocha dark)
mapped onto Tailwind v4 utilities via @theme inline so colours switch at
runtime with [data-theme]. One blue accent; --ctp-green/-yellow/-red stay
reserved for pass/warning/fail (read by gradeColor). Latte warning yellow
darkened for contrast on white. Neutral system sans, tabular-nums helper,
visible focus ring. app.html bootstrap bg now white/Mocha; dropped the
JetBrains Mono web font.

Claude-Session: https://claude.ai/code/session_013Tk2vvh35kVV1JSkMfQSzu
Sticky top nav with the four tool tabs (full labels, short labels on
mobile, horizontal scroll if tight), a sun/moon theme toggle and a compact
language select. Layout applies the active theme + lang to <html> and wraps
each tool in a centered main; footer shows version/commit/build date.

BREAKING CHANGE: The previous mobile off-canvas navigation drawer, the
keyboard-navigable language dropdown, and the page fade-in / transition
animations are removed in favour of a simpler top bar and a native language
select. (The mobile drawer is reinstated in a later commit.)

Claude-Session: https://claude.ai/code/session_013Tk2vvh35kVV1JSkMfQSzu
Page wrapper (title/subtitle, narrow/wide), Button (primary/secondary/
ghost/danger), NumberField (numericInput + clampInput actions), RoundingSelect,
ShareButton (copy with feedback + prompt fallback), ClearButton (two-step
confirm), ResultBar + StatusChip for understated inline results, EmptyState
and ShortcutHint. Button/input/label base styles live in an app.css
component layer driven by the design tokens.

Claude-Session: https://claude.ai/code/session_013Tk2vvh35kVV1JSkMfQSzu
Live result from (points × 5 / max) + 1 with the shared rounding select,
share and two-step clear. Subtle formula display, understated inline result
coloured green/yellow/red with a pass/fail chip, and the invalid /
out-of-range states. Inputs and rounding persist via the settings store;
share payloads round-trip on mount.

Claude-Session: https://claude.ai/code/session_013Tk2vvh35kVV1JSkMfQSzu
Recursive grade rows with nested sub-grades (parent grade derived from its
children), drag-to-reorder within each sibling list, and Ctrl+Enter /
Ctrl+Delete to add and remove the focused row. Live weighted average with
the shared rounding select, understated pass/fail result, CSV export
(disabled until there is exportable data), share and two-step clear. State
persists via the grades store; share payloads hydrate on mount.

BREAKING CHANGE: The rebuilt Average drops the per-row delta chips (each
grade's difference from the running average) and the grade-input border
colour coding that the previous UI provided.

Claude-Session: https://claude.ai/code/session_013Tk2vvh35kVV1JSkMfQSzu
Pulls the weighted sums from the Average grades and solves for the grade
needed in every future exam to reach a target average, under the stated
assumption of the same grade in all of them. Handles already-achieved
(g<=1), impossible (g>6, with best-attainable at 6.0) and the no-grades /
invalid-target states. Future exams add/remove with Ctrl+Enter / Ctrl+Delete,
new stores/needed.ts persists target + exams, and share payloads carry the
grade snapshot so links reproduce.

BREAKING CHANGE: The Required Grade tool's saved state (swiss-grades-needed)
adopts a new shape; state written by the previous version is not read back and
falls back to defaults.

Claude-Session: https://claude.ai/code/session_013Tk2vvh35kVV1JSkMfQSzu
Apprenticeship preset + path selectors (path shown only when the preset has
more than one), localized component cards with direct grade entry or
expandable part-grades, and the Variant select for ABU/eGK modes (including
dispensed). Live final grade via evaluateQV with fall-grade status per
component, pass/fail/pending result, failed-fall-grade and required-grade
guidance, plus the localized overview and sources. State persists via the qv
store and share payloads round-trip on mount.

BREAKING CHANGE: The rebuilt QV drops the completion progress bar, the visual
preset-picker modal (now a plain select), the sticky desktop column header,
and the per-variant mode descriptions from the previous UI.

Claude-Session: https://claude.ai/code/session_013Tk2vvh35kVV1JSkMfQSzu
Ghost buttons (add/details/clear idle) get a hairline border so they read as
buttons; the QV Details toggle gains a chevron. QV clear now uses the shared
'Clear all' label for consistency with the other tools. QV component rows
stack the label full-width on mobile so the grade input no longer overlaps
it. Average grade/weight inputs use the short placeholders (no truncation),
and the nav switches to short labels below md so 768 no longer clips.

Claude-Session: https://claude.ai/code/session_013Tk2vvh35kVV1JSkMfQSzu
Arlind-dev added 17 commits July 1, 2026 14:02
Full labels overflowed at 768; switch the nav to short labels below lg so the
wordmark, tabs and settings fit comfortably from 360 up to ~1024, with full
labels on wide screens.

Claude-Session: https://claude.ai/code/session_013Tk2vvh35kVV1JSkMfQSzu
Re-add three niceties from the old UI: ShareButton now tries the native
navigator.share() sheet first (clipboard, then prompt as fallbacks); QV
component descriptions return as a hover tooltip on the label; and QV weights
are shown as each component's share of the active weight, so BM / dispensed
tracks read correctly (e.g. IPA 57.1% / IK 42.9% over 70%) while the regular
track is unchanged.

Claude-Session: https://claude.ai/code/session_013Tk2vvh35kVV1JSkMfQSzu
- Re-add the mobile hamburger off-canvas nav drawer (slide-in panel, backdrop,
  Escape/backdrop/link close); inline tabs now show from sm up.
- Gate tool + nav-label content on hydration so non-German users no longer see
  a flash of the prerendered default locale.
- Clamp grade weights to 1-100 (Average rows and Target future exams).
- Fix the delete shortcut on macOS: accept Cmd+Backspace (without breaking
  Ctrl+Backspace word-delete on Windows) and show the platform hint as Cmd/erase.

Claude-Session: https://claude.ai/code/session_013Tk2vvh35kVV1JSkMfQSzu
The sun/moon icon was rendered from the theme store, which is 'latte' during
the prerender, so dark-mode users saw the light-mode icon until hydration.
Render both icons and let CSS pick via html[data-theme] (set before first
paint in app.html), so the correct icon shows immediately with no flash.

Claude-Session: https://claude.ai/code/session_013Tk2vvh35kVV1JSkMfQSzu
- Align header, content and footer to one centered max-w-3xl column so the
  whole page reads as centered; top nav uses short labels (full names stay in
  the drawer), and the wordmarks are brand-accent.
- Make toolbar buttons consistent: ghost buttons now use the same text colour
  as secondary ones (they were muted, which read as a different/lighter font).
- Rebuild the footer as a proper multi-column layout (brand + version badge /
  commit / date, Tools and Project link columns) modelled on osc-skins-ui,
  centered on mobile; add the footer i18n strings for all four locales.
- Fix a cascade bug: the global a{color:accent} was unlayered and overrode
  text-muted/text-accent on links (nav tabs and footer links were all blue);
  move it into @layer base so utilities win.

Claude-Session: https://claude.ai/code/session_013Tk2vvh35kVV1JSkMfQSzu
- StatusChip: drop the filled pill; a small coloured dot + coloured text reads
  cleaner and less generic.
- Footer build metadata: remove the dot-bullet separator and the version pill;
  make the whole 'version commit date' line one link to the GitHub release.
- ShortcutHint: larger, filled keycaps with a darker label so ⌘/⌫ are legible
  on macOS, and hide the whole hint on mobile (no keyboard there).

Claude-Session: https://claude.ai/code/session_013Tk2vvh35kVV1JSkMfQSzu
…e labels

- Center the nav tabs between the wordmark and the language/theme controls.
- StatusChip: drop the coloured dot; show just green/red/yellow (or muted)
  text so a status reads as a word, not a badge.
- Reword the fall-grade status: 'met' / 'not met' (DE 'Fallnote erfüllt' /
  'nicht erfüllt', FR 'suffisante' / 'insuffisante', IT 'sufficiente' /
  'insufficiente') instead of 'ok' / '< 4.0'.

Claude-Session: https://claude.ai/code/session_013Tk2vvh35kVV1JSkMfQSzu
- Let the QV fall-grade status size to its content and never wrap, so
  'Fallnote nicht erfüllt' stays on one line (verified no overflow at 360 in
  de/fr/it).
- Footer version line now links to the repository root instead of the release
  tag.

Claude-Session: https://claude.ai/code/session_013Tk2vvh35kVV1JSkMfQSzu
Split the advisory sentence onto its own line and lay the sources out as a
wrapped, generously-spaced list of underlined links instead of a single
comma-jammed run, so each source is easy to tell apart.

Claude-Session: https://claude.ai/code/session_013Tk2vvh35kVV1JSkMfQSzu
… rows

- Weight inputs now use a language-independent '%' suffix (NumberField gains a
  suffix adornment) instead of the long '(%)' word that overflowed in de/fr/it
  on the Average and Target pages.
- Restore the informative grade placeholder 'Note (1.00 - 6.00)' and widen the
  grade field so it fits, and add a 'Note' placeholder to the QV part-grade
  inputs.
- Average rows never wrap on mobile: hide the subject name and drag handle on
  small screens so grade + weight + actions stay on one line (no overflow,
  verified de/fr/it at 360).
- Default to 5 grade rows instead of 10 (store default, clear-all, and share
  hydration fallback).

BREAKING CHANGE: The default number of blank grade rows changes from 10 to 5,
and on small screens the subject-name field and drag handle are hidden.

Claude-Session: https://claude.ai/code/session_013Tk2vvh35kVV1JSkMfQSzu
- Grade inputs now normalise on blur: clamp to 1-6 and format to 2 decimals
  (5 -> 5.00, 7 -> 6.00, 0 -> 1.00, 4.1 -> 4.10). Applies to Average grades and
  sub-grades, the Target average, and QV component + part-grade inputs, via a
  new decimals option on NumberField / clampInput.
- Fix deep sub-grade nesting: a parent grade now resolves recursively from the
  bottom up, so a layer-4 leaf propagates all the way to layer 1 (previously it
  only read one level down and stalled).
- Target: drop the exam name field on mobile so the row stays on one line.

Claude-Session: https://claude.ai/code/session_013Tk2vvh35kVV1JSkMfQSzu
At the deepest layer the add-subgrade + button is hidden; render an
equal-size spacer in its place so the button column keeps its width and the
weight field no longer shifts toward the delete button.

Claude-Session: https://claude.ai/code/session_013Tk2vvh35kVV1JSkMfQSzu
Add a commitOnBlur mode to NumberField and enable it for the Average grade and
weight inputs, so a typed value only propagates to the parent grades and the
result once you leave the field (matching the old behaviour). Calculator and
Target inputs stay live.

Claude-Session: https://claude.ai/code/session_013Tk2vvh35kVV1JSkMfQSzu
The future-exam list (per-exam names and weights) was confusing: the result
was the same required grade for every exam, so names never mattered and the
weights were an advanced concept. Replace it with a single 'remaining exams'
stepper (each exam counts as one grade) and show one result line. Keeps the
already-achieved / not-reachable states, rounding, share and clear. The store
is now { target, count } (migrates the old shape); share still round-trips via
the futureExams array.

BREAKING CHANGE: The Required Grade tool no longer supports naming future exams
or giving them individual weights; it now takes a single count of remaining
exams, each weighted as one normal grade. Saved state and old share links are
migrated, but any per-exam weights they carried are dropped.

Claude-Session: https://claude.ai/code/session_013Tk2vvh35kVV1JSkMfQSzu
Delete the CSV export feature: the Export CSV button, the utils/export.ts
module, and the CSV i18n labels. Also remove code left unused after recent
refactors — the EmptyState component, utils/focus.ts, the gradeColor() helper,
and 37 orphaned message keys per locale (needed-tool leftovers, QV
overview/track/header strings, short placeholders, calculate buttons, and the
moved QV clear labels).

BREAKING CHANGE: The Grade Average CSV export is removed.

Claude-Session: https://claude.ai/code/session_013Tk2vvh35kVV1JSkMfQSzu
…rload

roundToHalf had no callers. computeNeededGrade's componentModes|number union
and targetGrade param served a calling convention nothing used; target is
always the passing grade. Collapse to a plain modes map.
Remove unused exports (getQVTrack, resetQV, KNOWN_STORAGE_KEYS,
QVTranslationField) and the dead index/isSub bits in GradeRow. Hoist the
duplicated parent-grade normalization into grading.normalizeGrades and reuse it
in the average/needed pages and GradeRow. Trim restatement/section-marker
comments.

Claude-Session: https://claude.ai/code/session_01EJFCCxeKiB5XMyddwG4S2j
@Arlind-dev Arlind-dev changed the title UI overhaul: calm, consistent, responsive redesign Rework: calm design system, rebuilt tools, shareable links Jul 1, 2026
Verified findings from a next-vs-main review:
- share: guard oversized URLs on send, clear ?share= after load so a
  refresh no longer clobbers edits, reject out-of-range shared grades,
  announce copy status via aria-live
- qv: restore the per-preset description dropped in the rebuild
- grades: guard missing subgrades in normalizeGrades
- needed: resync the remaining-exams field on blur
- centralize grade tone/pass logic in gradeTone/isPassing/toneColor;
  drop dead .btn-primary variant and stale comments

Claude-Session: https://claude.ai/code/session_01EJFCCxeKiB5XMyddwG4S2j
@Arlind-dev Arlind-dev marked this pull request as ready for review July 1, 2026 12:34
@Arlind-dev Arlind-dev changed the title Rework: calm design system, rebuilt tools, shareable links feat!: calm design system, rebuilt tools, shareable links Jul 1, 2026
@Arlind-dev Arlind-dev merged commit 894f6a8 into main Jul 1, 2026
6 checks passed
@Arlind-dev Arlind-dev deleted the next branch July 1, 2026 12:35
@arlind-bot

arlind-bot Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 2.0.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

@arlind-bot arlind-bot Bot added the released label Jul 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant