Files
F-r-ldraledigplannerare/JUNIOR_styling_plan.md
T
2026-06-14 13:05:53 +02:00

10 KiB
Raw Blame History

Junior Developer — Styling Plan

Owner: Junior developer (the filename says it — this is frontend craft, not backend) Status: Ready to implement (best after frontend_mvp_complete.md Tier 1, so the header/nav exist to style; the foundation in S1 can start anytime) Goal: Turn the unstyled MVP into a calm, trustworthy, readable app — without rewriting components.

Big win you already have: every component uses consistent BEM-style class names (dashboard__section, calendar__grid, progress-chart__fill, settings-page__message--success, …) but there is no CSS at all and nothing is imported. So you can style the entire app from stylesheets keyed to those existing classes with almost zero JSX changes. Don't refactor markup to style it — write CSS that targets what's there.


S0 — Decisions (don't introduce a new build dependency)

  • Use plain CSS with design tokens (CSS custom properties). ARCHITECTURE already anticipates src/styles/global.css + src/styles/components.module.css. Match that.
  • Do NOT add Tailwind / styled-components / a UI kit mid-MVP. They'd mean rewriting every className and adding build config — high churn, no payoff right now. (If the team later wants a framework, that's a separate, deliberate migration.)
  • Keep it light: one global stylesheet for tokens + base + layout + shared component classes. Reach for CSS Modules (*.module.css) only if a component needs scoping that the BEM names don't already give you.
  • Tone: this app touches family + money, so aim for calm and reassuring, not flashy — generous whitespace, soft neutral background, one trustworthy accent color, clear hierarchy. ARCHITECTURE calls the calendar "Google Calendar-like," so use that as the visual north star for the calendar.

S1 — Foundation: tokens, reset, typography

Create src/styles/global.css and import it once at the top of src/main.tsx (import './styles/global.css').

Define design tokens as CSS variables on :root so everything else references them (change once, update everywhere). Starter set — tune the palette to taste:

:root {
  /* Color */
  --c-bg: #f7f8fa;
  --c-surface: #ffffff;
  --c-border: #e3e6eb;
  --c-text: #1c2733;
  --c-text-muted: #5b6675;
  --c-accent: #2f6f4f;        /* calm green — trustworthy, on-brand for "leave/time at home" */
  --c-accent-weak: #e8f1ec;
  --c-danger: #b3261e;
  --c-danger-weak: #fdeceb;
  --c-success: #1e7d4f;
  --c-success-weak: #e7f4ec;
  --c-warning: #8a6d1f;

  /* Spacing scale (8px base) */
  --sp-1: 4px; --sp-2: 8px; --sp-3: 12px; --sp-4: 16px;
  --sp-5: 24px; --sp-6: 32px; --sp-8: 48px;

  /* Type scale */
  --fs-sm: 0.875rem; --fs-base: 1rem; --fs-lg: 1.25rem;
  --fs-xl: 1.5rem; --fs-2xl: 2rem;
  --lh: 1.5;

  /* Shape & depth */
  --radius: 8px;
  --shadow-sm: 0 1px 2px rgba(16,24,40,0.06);
  --shadow-md: 0 4px 12px rgba(16,24,40,0.08);

  --maxw: 960px;             /* content column width */
  color-scheme: light;
}

Then a small reset + base: *, *::before, *::after { box-sizing: border-box }, zero default margins, set body { font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; background: var(--c-bg); color: var(--c-text); line-height: var(--lh); }, a sensible type scale on h1–h3, and a centered content container.

Acceptance: importing one file visibly transforms the whole app — proper font, background, spacing, readable headings — with no component edits.


Targets the layout classes ([data-testid="layout"], [data-testid="header"], header-nav, [data-testid="footer"], .dashboard, .settings-page, etc.) and the header built in frontend_mvp_complete.md T1.1.

Do:

  • Sticky top header: app name/title left, primary nav center/right (Översikt / Kalender / Tips / Inställningar), and the user + child selector + "Logga ut" on the far right. Give the active nav link a clear state (use NavLink's active class).
  • Style the child selector as a clean dropdown so "which child am I viewing" reads at a glance.
  • Constrain main content to --maxw, centered, with comfortable page padding.
  • Quiet footer (muted text, top border).
  • Make every page sit inside this shell consistently (Layout already wraps them).

Acceptance: A coherent top bar on every page with obvious current-page, current-child, and logout; content is centered and comfortably padded.


S3 — Cards, sections, metrics, tables

Targets .dashboard__section, [data-testid="overview-metrics"], .days-breakdown__table, .dashboard__break-list/__break-item.

Do:

  • Turn each dashboard__section / settings-page__section into a card: background: var(--c-surface), border: 1px solid var(--c-border), border-radius: var(--radius), padding --sp-5, box-shadow: var(--shadow-sm), vertical rhythm between cards.
  • Overview metrics: lay the metric list out as a responsive row of stat blocks — big number (--fs-2xl, accent or bold), small muted label beneath. This is the hero of the dashboard; make the numbers legible.
  • Tables (days-breakdown__table): full width, subtle row separators, padded cells, muted bold header row, right-align numeric columns.
  • Style the provenance/freshness line from polish P1 as small muted text.

Acceptance: The dashboard reads as a set of clean cards; the key day numbers stand out; tables are tidy.


S4 — Buttons, forms, inputs, messages

Targets .form-group, .error-message, .settings-page__message--success/--error, .parent-profile-input*, .planning-goal-selector*, .break-periods-input*, .vacation-days-input (and the bare <button>s on the dashboard).

Do:

  • A shared button style: primary (accent bg, white text), secondary/ghost (border, surface bg), and a disabled state (the FK button is disabled until a child is selected — make that visually obvious). Add :hover, :active, and a visible :focus-visible outline.
  • Inputs/selects: consistent height, padding, border, radius; clear :focus ring using the accent; label spacing via .form-group.
  • Message banners: --success-weak/--danger-weak backgrounds with matching text + a left accent border. Style error-message and the settings save messages.
  • Fieldsets (planning goal, parent profile) as labeled groups with breathing room.

Acceptance: All buttons/inputs share one consistent look; success/error feedback is color-coded and unmistakable; focus states are visible for keyboard users.


S5 — Calendar (the "Google Calendar-like" view) — ⚠️ SUPERSEDED

SUPERSEDED by junior_calendar_fix.md. The calendar is being fixed as its own focused task, which already covers the 7-column grid, day cells, today/weekend/adjacent-month states, the legend, plus correctness fixes (Monday-first Swedish week, default-to-current-month) and tests. Do not restyle the calendar from this section — you'll collide with that work.

Only remaining action here: once junior_calendar_fix.md has shipped and global.css tokens (S1) exist, reconcile the calendar's co-located calendar.css to reference the tokens (--c-*, --sp-*, --radius) and define per-type color tokens (--type-sjukpenning, --type-lagsta, --type-vacation, plus a dubbeldagar marker) so cell chips and the legend stay in sync with the rest of the app. No layout or markup changes.

The original styling targets, kept for reference during that token reconciliation: .calendar, .calendar__header, .calendar__grid, .calendar__weekday, .calendar-day*, .calendar-event*, .calendar__legend*.

Acceptance: Calendar colors/spacing reference S1 tokens; leave-type colors match between cells and legend. (Layout, states, and behavior are owned by junior_calendar_fix.md.)


S6 — Modal & loading states

Targets the QRCodeModal (built in the MVP rounds) and LoadingSpinner.

Do:

  • Modal: centered card over a dimmed full-screen overlay (position: fixed, semi-transparent backdrop), --shadow-md, max-width, padding; the QR image sized large and crisp (don't let it blur — it must be scannable); clear status text and a cancel/close button. Disable/hide close while a session is running (the component already gates this).
  • Spinner: simple CSS keyframe spinner using the accent, with the muted message beneath, centered in the content area.

Acceptance: The FK fetch modal looks intentional and the QR is comfortably scannable; loading states are centered and calm.


S7 — Responsive & accessibility pass

Do:

  • One mobile breakpoint (~640px): header nav collapses to a simple stacked/wrapped layout; metric row and form grids stack to one column; calendar stays usable (allow horizontal scroll or shrink cells — don't break the 7-column grid).
  • A11y: ensure text/background contrast meets WCAG AA (check the accent on white and white on accent), keep a visible :focus-visible outline everywhere, and respect @media (prefers-reduced-motion: reduce) by disabling the spinner/transition animations.

Acceptance: Usable on a narrow viewport without horizontal chaos; keyboard focus is always visible; contrast passes AA; reduced-motion is honored.


Sequencing & guardrails

  1. S1 → S2 → S3 → S4 give the biggest visible lift fastest (foundation + shell + cards + controls).
  2. S5 (calendar) is superseded by junior_calendar_fix.md — which is being delivered first, before this styling plan. Don't restyle the calendar here; the only S5 task left is the token reconciliation noted in that section, done after both this plan's S1 and the calendar fix have landed.
  3. S6, S7 finish it off.
  • Don't change component markup or data-testids to style — tests rely on them. Style via the existing classes; only add a className where one is genuinely missing.
  • Keep all colors/spacing referencing the tokens from S1 — no hardcoded hex scattered through the file.
  • After each section: npm run lint, npm test (should stay green — CSS shouldn't break tests; if a test asserts on inline styles, reconcile), and eyeball the page.

Out of scope: dark mode, animations beyond basic transitions, a component-library migration, custom fonts/branding assets — all separate later passes if desired.