diff --git a/frontend/src/components/calendar/period-popover.css b/frontend/src/components/calendar/period-popover.css index f8f895b..d83cb8b 100644 --- a/frontend/src/components/calendar/period-popover.css +++ b/frontend/src/components/calendar/period-popover.css @@ -4,7 +4,7 @@ left: 0; right: 0; bottom: 0; - background-color: rgba(0, 0, 0, 0.5); + background-color: var(--overlay); display: flex; align-items: center; justify-content: center; @@ -12,9 +12,9 @@ } .period-popover__content { - background-color: #fff; - border-radius: 8px; - box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15); + background-color: var(--c-surface); + border-radius: var(--radius); + box-shadow: var(--shadow-md); max-width: 480px; width: 90%; max-height: 80vh; @@ -25,166 +25,186 @@ display: flex; justify-content: space-between; align-items: center; - padding: 20px; - border-bottom: 1px solid #e0e0e0; + padding: var(--sp-5); + border-bottom: 1px solid var(--c-border); } .period-popover__header h2 { margin: 0; - font-size: 18px; - font-weight: 600; + font-size: var(--fs-xl); + font-weight: 700; } .period-popover__close { background: none; border: none; - font-size: 28px; + font-size: var(--fs-xl); cursor: pointer; padding: 0; - color: #666; + color: var(--c-text-muted); transition: color 0.2s; } .period-popover__close:hover { - color: #000; + color: var(--c-text); +} + +.period-popover__close:focus-visible { + outline: 2px solid var(--c-accent); + outline-offset: 2px; } .period-popover__body { - padding: 20px; + padding: var(--sp-5); } .period-popover__error { - background-color: #ffebee; - color: #c62828; - padding: 12px; - border-radius: 4px; - margin-bottom: 16px; - font-size: 14px; + background-color: var(--c-danger-weak); + color: var(--c-danger); + padding: var(--sp-3); + border-radius: var(--radius-sm); + margin-bottom: var(--sp-4); + font-size: var(--fs-sm); } .period-popover__adhoc-notice { - font-size: 13px; - color: #666; - margin: 0 0 12px 0; + font-size: var(--fs-sm); + color: var(--c-text-muted); + margin: 0 0 var(--sp-3) 0; } .period-popover__date-range { - font-size: 15px; + font-size: var(--fs-base); font-weight: 500; - color: #333; + color: var(--c-text); } .period-popover__field { - margin-bottom: 16px; + margin-bottom: var(--sp-4); } .period-popover__label { display: block; - font-size: 14px; + font-size: var(--fs-sm); font-weight: 500; - margin-bottom: 6px; - color: #333; + margin-bottom: var(--sp-2); + color: var(--c-text); } .period-popover__input, .period-popover__select { width: 100%; - padding: 8px 10px; - border: 1px solid #d0d0d0; - border-radius: 4px; - font-size: 14px; - background-color: #fff; + padding: var(--sp-2) var(--sp-3); + border: 1px solid var(--c-border); + border-radius: var(--radius-sm); + font-size: var(--fs-sm); + background-color: var(--c-surface); +} + +.period-popover__input:focus, +.period-popover__select:focus { + outline: none; + border-color: var(--c-accent); + box-shadow: 0 0 0 3px var(--c-accent-weak); } .period-popover__input:disabled, .period-popover__select:disabled { - background-color: #f5f5f5; + background-color: var(--c-bg); cursor: not-allowed; } .period-popover__fieldset { border: none; padding: 0; - margin: 0 0 16px 0; + margin: 0 0 var(--sp-4) 0; } .period-popover__radio-label, .period-popover__checkbox-label { display: flex; align-items: center; - font-size: 14px; + font-size: var(--fs-sm); cursor: pointer; user-select: none; - margin-bottom: 8px; + margin-bottom: var(--sp-2); } .period-popover__radio-label input, .period-popover__checkbox-label input { - margin-right: 8px; + margin-right: var(--sp-2); cursor: pointer; } .period-popover__footer { display: flex; - gap: 8px; + gap: var(--sp-2); justify-content: flex-end; - padding: 16px 20px; - border-top: 1px solid #e0e0e0; - background-color: #fafafa; + padding: var(--sp-4) var(--sp-5); + border-top: 1px solid var(--c-border); + background-color: var(--c-bg); } .period-popover__button { - padding: 8px 16px; - border-radius: 4px; - font-size: 14px; + padding: var(--sp-2) var(--sp-4); + border-radius: var(--radius-sm); + font-size: var(--fs-sm); font-weight: 500; cursor: pointer; border: none; transition: background-color 0.2s; } +.period-popover__button:focus-visible { + outline: 2px solid var(--c-accent); + outline-offset: 2px; +} + .period-popover__button:disabled { opacity: 0.6; cursor: not-allowed; } .period-popover__button--primary { - background-color: #0066cc; - color: #fff; + background-color: var(--c-accent); + color: var(--c-surface); } .period-popover__button--primary:hover:not(:disabled) { - background-color: #0052a3; + background-color: var(--c-accent-hover); } .period-popover__button--secondary { - background-color: #e0e0e0; - color: #333; + background-color: var(--c-bg); + color: var(--c-text); + border: 1px solid var(--c-border); } .period-popover__button--secondary:hover:not(:disabled) { - background-color: #d0d0d0; + background-color: var(--c-border); } .period-popover__button--danger { - background-color: #d32f2f; - color: #fff; + background-color: var(--c-danger-weak); + color: var(--c-danger); + border: 1px solid var(--c-danger); margin-right: auto; } .period-popover__button--danger:hover:not(:disabled) { - background-color: #b71c1c; + background-color: var(--c-danger); + color: var(--c-surface); } .period-popover__delete-confirm { display: flex; align-items: center; - gap: 12px; + gap: var(--sp-3); width: 100%; } .period-popover__delete-confirm-text { - font-size: 14px; - color: #333; + font-size: var(--fs-sm); + color: var(--c-text); margin-right: auto; } diff --git a/frontend/src/components/calendar/preset-rail.css b/frontend/src/components/calendar/preset-rail.css index 8c1e29f..25c420c 100644 --- a/frontend/src/components/calendar/preset-rail.css +++ b/frontend/src/components/calendar/preset-rail.css @@ -1,30 +1,30 @@ .preset-rail { width: 200px; flex-shrink: 0; - padding: 16px; - background-color: #f9f9f9; - border: 1px solid #e0e0e0; - border-radius: 4px; + padding: var(--sp-4); + background-color: var(--c-surface); + border-radius: var(--radius); + box-shadow: var(--shadow-sm); align-self: flex-start; } .preset-rail__heading { - margin: 0 0 4px 0; - font-size: 14px; + margin: 0 0 var(--sp-1) 0; + font-size: var(--fs-sm); font-weight: 600; - color: #333; + color: var(--c-text); } .preset-rail__hint { - margin: 0 0 12px 0; - font-size: 12px; - color: #666; + margin: 0 0 var(--sp-3) 0; + font-size: var(--fs-xs); + color: var(--c-text-muted); } .preset-rail__buttons { display: flex; flex-direction: column; - gap: 8px; + gap: var(--sp-2); } .preset-rail__button { @@ -32,18 +32,18 @@ flex-direction: column; align-items: flex-start; gap: 2px; - padding: 10px 12px; - border: 1px solid #d0d0d0; - border-radius: 4px; - background-color: #fff; + padding: var(--sp-2) var(--sp-3); + border: 1px solid var(--c-border); + border-radius: var(--radius-sm); + background-color: var(--c-surface); cursor: pointer; text-align: left; transition: background-color 0.2s, border-color 0.2s; } .preset-rail__button:hover:not(:disabled) { - background-color: #f0f8ff; - border-color: #0066cc; + background-color: var(--c-accent-weak); + border-color: var(--c-accent); } .preset-rail__button:disabled { @@ -52,12 +52,12 @@ } .preset-rail__button-name { - font-size: 14px; + font-size: var(--fs-sm); font-weight: 600; - color: #333; + color: var(--c-text); } .preset-rail__button-detail { - font-size: 11px; - color: #666; + font-size: var(--fs-2xs); + color: var(--c-text-muted); } diff --git a/frontend/src/components/layout/layout.css b/frontend/src/components/layout/layout.css index 9ae98e6..6de4a4f 100644 --- a/frontend/src/components/layout/layout.css +++ b/frontend/src/components/layout/layout.css @@ -11,7 +11,7 @@ max-width: var(--maxw); width: 100%; margin: 0 auto; - padding: var(--sp-5); + padding: var(--sp-6) var(--sp-5); } /* Dashboard & Pages */ @@ -23,16 +23,31 @@ width: 100%; } +/* Sections separate by whitespace and a hairline rule, not a stack of + * bordered/shadowed cards — every section on a page looking equally "boxed" + * flattens the hierarchy the headline figures above them are meant to have. + * The `
` accordions (plan-fold, dashboard__more-details) keep their + * own bordered treatment in components.css since they're interactive + * disclosure controls, not passive content groups. */ .dashboard__section, .settings-page__section, .calendar__section, .tips-page__section { - background-color: var(--c-surface); - border: 1px solid var(--c-border); - border-radius: var(--radius); - padding: var(--sp-5); - margin-bottom: var(--sp-5); - box-shadow: var(--shadow-sm); + background-color: transparent; + border: none; + border-top: 1px solid var(--c-border); + border-radius: 0; + padding: var(--sp-6) 0 0; + margin-bottom: var(--sp-6); + box-shadow: none; +} + +.dashboard__section:first-child, +.settings-page__section:first-child, +.calendar__section:first-child, +.tips-page__section:first-child { + border-top: none; + padding-top: 0; } .dashboard__section:last-child, diff --git a/frontend/src/components/modals/manual-fetch-modal.css b/frontend/src/components/modals/manual-fetch-modal.css index 2311a5a..f53461c 100644 --- a/frontend/src/components/modals/manual-fetch-modal.css +++ b/frontend/src/components/modals/manual-fetch-modal.css @@ -4,7 +4,7 @@ left: 0; right: 0; bottom: 0; - background-color: rgba(0, 0, 0, 0.5); + background-color: var(--overlay); display: flex; align-items: center; justify-content: center; @@ -12,9 +12,9 @@ } .manual-fetch-modal__content { - background-color: #fff; - border-radius: 8px; - box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15); + background-color: var(--c-surface); + border-radius: var(--radius); + box-shadow: var(--shadow-md); max-width: 600px; width: 90%; max-height: 85vh; @@ -25,53 +25,58 @@ display: flex; justify-content: space-between; align-items: center; - padding: 20px; - border-bottom: 1px solid #e0e0e0; + padding: var(--sp-5); + border-bottom: 1px solid var(--c-border); } .manual-fetch-modal__header h2 { margin: 0; - font-size: 18px; - font-weight: 600; + font-size: var(--fs-xl); + font-weight: 700; } .manual-fetch-modal__close { background: none; border: none; - font-size: 28px; + font-size: var(--fs-xl); cursor: pointer; padding: 0; - color: #666; + color: var(--c-text-muted); transition: color 0.2s; } .manual-fetch-modal__close:hover { - color: #000; + color: var(--c-text); +} + +.manual-fetch-modal__close:focus-visible { + outline: 2px solid var(--c-accent); + outline-offset: 2px; } .manual-fetch-modal__body { - padding: 20px; + padding: var(--sp-5); } .manual-fetch-modal__description { - font-size: 14px; - color: #666; - margin: 0 0 16px 0; + font-size: var(--fs-sm); + color: var(--c-text-muted); + margin: 0 0 var(--sp-4) 0; } .manual-fetch-modal__error { - background-color: #ffebee; - color: #c62828; - padding: 12px; - border-radius: 4px; - margin-bottom: 16px; - font-size: 14px; + background-color: var(--c-danger-weak); + color: var(--c-danger); + padding: var(--sp-3); + border-radius: var(--radius-sm); + margin-bottom: var(--sp-4); + font-size: var(--fs-sm); } .manual-fetch-modal__section { - margin-bottom: 20px; - padding-bottom: 20px; - border-bottom: 1px solid #f0f0f0; + margin-bottom: var(--sp-5); + padding-bottom: var(--sp-5); + border-bottom: 1px solid var(--c-border); } .manual-fetch-modal__section:last-child { @@ -81,85 +86,97 @@ } .manual-fetch-modal__section h3 { - margin: 0 0 12px 0; - font-size: 14px; + margin: 0 0 var(--sp-3) 0; + font-size: var(--fs-sm); font-weight: 600; - color: #333; + color: var(--c-text); } .manual-fetch-modal__field { - margin-bottom: 12px; + margin-bottom: var(--sp-3); } .manual-fetch-modal__field label { display: block; - font-size: 13px; + font-size: var(--fs-xs); font-weight: 500; - margin-bottom: 4px; - color: #555; + margin-bottom: var(--sp-1); + color: var(--c-text-muted); } .manual-fetch-modal__field input { width: 100%; - padding: 8px 10px; - border: 1px solid #d0d0d0; - border-radius: 4px; - font-size: 14px; + padding: var(--sp-2) var(--sp-3); + border: 1px solid var(--c-border); + border-radius: var(--radius-sm); + font-size: var(--fs-sm); box-sizing: border-box; } +.manual-fetch-modal__field input:focus { + outline: none; + border-color: var(--c-accent); + box-shadow: 0 0 0 3px var(--c-accent-weak); +} + .manual-fetch-modal__field input:disabled { - background-color: #f5f5f5; + background-color: var(--c-bg); cursor: not-allowed; } .manual-fetch-modal__info { - font-size: 13px; - color: #666; - margin-top: 8px; - padding: 8px; - background-color: #f9f9f9; - border-radius: 4px; + font-size: var(--fs-xs); + color: var(--c-text-muted); + margin-top: var(--sp-2); + padding: var(--sp-2); + background-color: var(--c-bg); + border-radius: var(--radius-sm); } .manual-fetch-modal__footer { display: flex; - gap: 8px; + gap: var(--sp-2); justify-content: flex-end; - padding: 16px 20px; - border-top: 1px solid #e0e0e0; - background-color: #fafafa; + padding: var(--sp-4) var(--sp-5); + border-top: 1px solid var(--c-border); + background-color: var(--c-bg); } .manual-fetch-modal__button { - padding: 8px 16px; - border-radius: 4px; - font-size: 14px; + padding: var(--sp-2) var(--sp-4); + border-radius: var(--radius-sm); + font-size: var(--fs-sm); font-weight: 500; cursor: pointer; border: none; transition: background-color 0.2s; } +.manual-fetch-modal__button:focus-visible { + outline: 2px solid var(--c-accent); + outline-offset: 2px; +} + .manual-fetch-modal__button:disabled { opacity: 0.6; cursor: not-allowed; } .manual-fetch-modal__button--primary { - background-color: #0066cc; - color: #fff; + background-color: var(--c-accent); + color: var(--c-surface); } .manual-fetch-modal__button--primary:hover:not(:disabled) { - background-color: #0052a3; + background-color: var(--c-accent-hover); } .manual-fetch-modal__button--secondary { - background-color: #e0e0e0; - color: #333; + background-color: var(--c-bg); + color: var(--c-text); + border: 1px solid var(--c-border); } .manual-fetch-modal__button--secondary:hover:not(:disabled) { - background-color: #d0d0d0; + background-color: var(--c-border); } diff --git a/frontend/src/components/modals/planned-day-modal.css b/frontend/src/components/modals/planned-day-modal.css index 443fb8c..e5b0332 100644 --- a/frontend/src/components/modals/planned-day-modal.css +++ b/frontend/src/components/modals/planned-day-modal.css @@ -4,7 +4,7 @@ left: 0; right: 0; bottom: 0; - background-color: rgba(0, 0, 0, 0.5); + background-color: var(--overlay); display: flex; align-items: center; justify-content: center; @@ -12,9 +12,9 @@ } .planned-day-modal__content { - background-color: #fff; - border-radius: 8px; - box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15); + background-color: var(--c-surface); + border-radius: var(--radius); + box-shadow: var(--shadow-md); max-width: 500px; width: 90%; max-height: 80vh; @@ -25,87 +25,98 @@ display: flex; justify-content: space-between; align-items: center; - padding: 20px; - border-bottom: 1px solid #e0e0e0; + padding: var(--sp-5); + border-bottom: 1px solid var(--c-border); } .planned-day-modal__header h2 { margin: 0; - font-size: 18px; - font-weight: 600; + font-size: var(--fs-xl); + font-weight: 700; } .planned-day-modal__close { background: none; border: none; - font-size: 28px; + font-size: var(--fs-xl); cursor: pointer; padding: 0; - color: #666; + color: var(--c-text-muted); transition: color 0.2s; } .planned-day-modal__close:hover { - color: #000; + color: var(--c-text); +} + +.planned-day-modal__close:focus-visible { + outline: 2px solid var(--c-accent); + outline-offset: 2px; } .planned-day-modal__body { - padding: 20px; + padding: var(--sp-5); } .planned-day-modal__date { - font-size: 14px; - color: #666; - margin: 0 0 16px 0; + font-size: var(--fs-sm); + color: var(--c-text-muted); + margin: 0 0 var(--sp-4) 0; font-weight: 500; } .planned-day-modal__error { - background-color: #ffebee; - color: #c62828; - padding: 12px; - border-radius: 4px; - margin-bottom: 16px; - font-size: 14px; + background-color: var(--c-danger-weak); + color: var(--c-danger); + padding: var(--sp-3); + border-radius: var(--radius-sm); + margin-bottom: var(--sp-4); + font-size: var(--fs-sm); } .planned-day-modal__field { - margin-bottom: 16px; + margin-bottom: var(--sp-4); } .planned-day-modal__label { display: block; - font-size: 14px; + font-size: var(--fs-sm); font-weight: 500; - margin-bottom: 6px; - color: #333; + margin-bottom: var(--sp-2); + color: var(--c-text); } .planned-day-modal__select { width: 100%; - padding: 8px 10px; - border: 1px solid #d0d0d0; - border-radius: 4px; - font-size: 14px; - background-color: #fff; + padding: var(--sp-2) var(--sp-3); + border: 1px solid var(--c-border); + border-radius: var(--radius-sm); + font-size: var(--fs-sm); + background-color: var(--c-surface); cursor: pointer; } +.planned-day-modal__select:focus { + outline: none; + border-color: var(--c-accent); + box-shadow: 0 0 0 3px var(--c-accent-weak); +} + .planned-day-modal__select:disabled { - background-color: #f5f5f5; + background-color: var(--c-bg); cursor: not-allowed; } .planned-day-modal__checkbox-label { display: flex; align-items: center; - font-size: 14px; + font-size: var(--fs-sm); cursor: pointer; user-select: none; } .planned-day-modal__checkbox-label input[type='checkbox'] { - margin-right: 8px; + margin-right: var(--sp-2); cursor: pointer; } @@ -114,23 +125,23 @@ } .planned-day-modal__hint { - font-size: 12px; - color: #999; - margin-top: 6px; - margin-left: 24px; + font-size: var(--fs-xs); + color: var(--c-text-faint); + margin-top: var(--sp-2); + margin-left: var(--sp-6); font-style: italic; } .planned-day-modal__pinned-notice { - font-size: 12px; - color: #666; - margin: -8px 0 16px 0; + font-size: var(--fs-xs); + color: var(--c-text-muted); + margin: calc(var(--sp-2) * -1) 0 var(--sp-4) 0; } .planned-day-modal__override-switch { display: flex; - gap: 16px; - margin-top: 6px; + gap: var(--sp-4); + margin-top: var(--sp-2); } .planned-day-modal__override-switch .planned-day-modal__checkbox-label { @@ -139,64 +150,72 @@ .planned-day-modal__footer { display: flex; - gap: 8px; + gap: var(--sp-2); justify-content: flex-end; - padding: 16px 20px; - border-top: 1px solid #e0e0e0; - background-color: #fafafa; + padding: var(--sp-4) var(--sp-5); + border-top: 1px solid var(--c-border); + background-color: var(--c-bg); } .planned-day-modal__button { - padding: 8px 16px; - border-radius: 4px; - font-size: 14px; + padding: var(--sp-2) var(--sp-4); + border-radius: var(--radius-sm); + font-size: var(--fs-sm); font-weight: 500; cursor: pointer; border: none; transition: background-color 0.2s; } +.planned-day-modal__button:focus-visible { + outline: 2px solid var(--c-accent); + outline-offset: 2px; +} + .planned-day-modal__button:disabled { opacity: 0.6; cursor: not-allowed; } .planned-day-modal__button--primary { - background-color: #0066cc; - color: #fff; + background-color: var(--c-accent); + color: var(--c-surface); } .planned-day-modal__button--primary:hover:not(:disabled) { - background-color: #0052a3; + background-color: var(--c-accent-hover); } .planned-day-modal__button--secondary { - background-color: #e0e0e0; - color: #333; + background-color: var(--c-bg); + color: var(--c-text); + border: 1px solid var(--c-border); } .planned-day-modal__button--secondary:hover:not(:disabled) { - background-color: #d0d0d0; + background-color: var(--c-border); } .planned-day-modal__button--danger { - background-color: #d32f2f; - color: #fff; + background-color: var(--c-danger-weak); + color: var(--c-danger); + border: 1px solid var(--c-danger); } .planned-day-modal__button--danger:hover:not(:disabled) { - background-color: #b71c1c; + background-color: var(--c-danger); + color: var(--c-surface); } .planned-day-modal__delete-confirm { display: flex; align-items: center; - gap: 12px; + gap: var(--sp-3); width: 100%; } .planned-day-modal__delete-confirm-text { - font-size: 14px; - color: #333; + font-size: var(--fs-sm); + color: var(--c-text); margin-right: auto; } diff --git a/frontend/src/components/modals/qrcode-modal.css b/frontend/src/components/modals/qrcode-modal.css index d8333ec..4b91402 100644 --- a/frontend/src/components/modals/qrcode-modal.css +++ b/frontend/src/components/modals/qrcode-modal.css @@ -183,7 +183,7 @@ } .qrcode-modal__button--success:hover { - background-color: #0d6a37; + background-color: var(--c-success-hover); } .qrcode-modal__button--danger { @@ -192,7 +192,7 @@ } .qrcode-modal__button--danger:hover { - background-color: #8a1a15; + background-color: var(--c-danger-hover); } .qrcode-modal__button:disabled { diff --git a/frontend/src/pages/Auth.tsx b/frontend/src/pages/Auth.tsx index 46b5c7b..02b1deb 100644 --- a/frontend/src/pages/Auth.tsx +++ b/frontend/src/pages/Auth.tsx @@ -3,6 +3,7 @@ import { useNavigate } from 'react-router-dom' import { useAuth } from '@/context/AuthContext' import { useWorkspace } from '@/context/WorkspaceContext' import { useTranslation } from '@/i18n' +import './auth.css' export function Auth() { const { login, register, isAuthenticated } = useAuth() @@ -38,38 +39,45 @@ export function Auth() { } return ( -
-

{isRegister ? t('auth.createAccountTitle') : t('auth.loginTitle')}

-
void handleSubmit(e)}> - {isRegister && ( +
+
+

{t('app.name')}

+

{isRegister ? t('auth.createAccountTitle') : t('auth.loginTitle')}

+ void handleSubmit(e)}> + {isRegister && ( + setDisplayName(e.target.value)} + /> + )} setDisplayName(e.target.value)} + type="email" + data-testid="auth-email" + placeholder={t('auth.emailPlaceholder')} + value={email} + onChange={(e) => setEmail(e.target.value)} /> - )} - setEmail(e.target.value)} - /> - setPassword(e.target.value)} - /> - {error &&

{error}

} - + + - - +
) } diff --git a/frontend/src/pages/Dashboard.tsx b/frontend/src/pages/Dashboard.tsx index 5f050f2..1711015 100644 --- a/frontend/src/pages/Dashboard.tsx +++ b/frontend/src/pages/Dashboard.tsx @@ -229,23 +229,17 @@ export function Dashboard(): ReactNode { {age4Bars.length > 0 && } -
-
- - -
+
+ +
{selectedChild && ( diff --git a/frontend/src/pages/auth.css b/frontend/src/pages/auth.css new file mode 100644 index 0000000..73eccab --- /dev/null +++ b/frontend/src/pages/auth.css @@ -0,0 +1,56 @@ +/* Login/register — the app's front door, rendered outside (no + * header yet, there's no session). Previously a bare, unstyled
that + * relied entirely on the global element resets; this gives it the same + * centered-card treatment as the onboarding wizard it leads into. */ +.auth-page { + min-height: 100vh; + display: flex; + align-items: center; + justify-content: center; + padding: var(--sp-5); +} + +.auth-page__card { + width: 100%; + max-width: 380px; +} + +.auth-page__brand { + margin: 0 0 var(--sp-5) 0; + font-size: var(--fs-sm); + font-weight: 600; + color: var(--c-text-muted); + text-align: center; + text-transform: uppercase; + letter-spacing: 0.04em; +} + +.auth-page__title { + text-align: center; + margin-bottom: var(--sp-5); +} + +.auth-page__form { + display: flex; + flex-direction: column; + gap: var(--sp-3); + margin-bottom: var(--sp-4); +} + +.auth-page__form button[type='submit'] { + margin-top: var(--sp-2); +} + +.auth-page__error { + margin: 0; + padding: var(--sp-3) var(--sp-4); + background-color: var(--c-danger-weak); + color: var(--c-danger); + border-left: 4px solid var(--c-danger); + border-radius: var(--radius-sm); + font-size: var(--fs-sm); +} + +.auth-page__switch { + width: 100%; +} diff --git a/frontend/src/pages/calendar.css b/frontend/src/pages/calendar.css index aaa69b5..f9a8e52 100644 --- a/frontend/src/pages/calendar.css +++ b/frontend/src/pages/calendar.css @@ -1,58 +1,53 @@ -.calendar { - max-width: 920px; - margin: 0 auto; - padding: 16px; -} - .calendar__header { display: flex; align-items: center; justify-content: space-between; - margin-bottom: 24px; + margin-bottom: var(--sp-5); } .calendar__header button { min-width: 36px; min-height: 36px; - padding: 8px 12px; + padding: var(--sp-2) var(--sp-3); background: none; color: var(--c-text); - border: 1px solid #d0d0d0; - border-radius: 4px; - font-size: 18px; + border: 1px solid var(--c-border); + border-radius: var(--radius-sm); + font-size: var(--fs-lg); cursor: pointer; - transition: background-color 0.2s; + transition: background-color 0.2s, border-color 0.2s; } .calendar__header button:hover { - background-color: #f0f0f0; + background-color: var(--c-bg); + border-color: var(--c-text-muted); } .calendar__title { margin: 0; - font-size: 20px; - font-weight: 600; - color: #333; + font-size: var(--fs-xl); + font-weight: 700; + color: var(--c-text); display: flex; - gap: 8px; + gap: var(--sp-2); align-items: center; justify-content: center; } .calendar__month-select, .calendar__year-select { - background: #fff; - border: 1px solid #d0d0d0; - color: #333; - font-size: 20px; - font-weight: 600; + background: var(--c-surface); + border: 1px solid var(--c-border); + color: var(--c-text); + font-size: var(--fs-xl); + font-weight: 700; cursor: pointer; - padding: 4px 8px; - border-radius: 4px; + padding: var(--sp-1) var(--sp-2); + border-radius: var(--radius-sm); transition: background-color 0.2s, border-color 0.2s, color 0.2s; appearance: none; padding-right: 28px; - background-image: url('data:image/svg+xml;utf8,'); + background-image: url('data:image/svg+xml;utf8,'); background-repeat: no-repeat; background-position: right 6px center; background-size: 12px; @@ -60,28 +55,28 @@ .calendar__month-select:hover, .calendar__year-select:hover { - background-color: #f0f0f0; - border-color: #0066cc; - color: #0066cc; + background-color: var(--c-bg); + border-color: var(--c-accent); + color: var(--c-accent); } .calendar__month-select:focus-visible, .calendar__year-select:focus-visible { - outline: 2px solid #0066cc; + outline: 2px solid var(--c-accent); outline-offset: 2px; - border-color: #0066cc; + border-color: var(--c-accent); } .calendar__month-select:active, .calendar__year-select:active { - background-color: #e0e0e0; + background-color: var(--c-border); } .calendar__grid { display: grid; grid-template-columns: repeat(7, 1fr); - gap: 8px; - margin-bottom: 8px; + gap: var(--sp-2); + margin-bottom: var(--sp-2); } /* The weeks themselves now live in a separate flex column below the header @@ -93,114 +88,114 @@ .calendar__weeks { display: flex; flex-direction: column; - gap: 8px; - margin-bottom: 24px; + gap: var(--sp-2); + margin-bottom: var(--sp-5); } .calendar__weekday { - padding: 12px 8px; + padding: var(--sp-3) var(--sp-2); text-align: center; font-weight: 600; - font-size: 13px; - color: #666; + font-size: var(--fs-xs); + color: var(--c-text-muted); text-transform: uppercase; - letter-spacing: 0.5px; + letter-spacing: 0.04em; } .calendar-day { min-height: 84px; - padding: 8px; - border: 1px solid #e0e0e0; - border-radius: 4px; - background-color: #fff; + padding: var(--sp-2); + border: 1px solid var(--c-border); + border-radius: var(--radius-sm); + background-color: var(--c-surface); cursor: pointer; display: flex; flex-direction: column; - transition: all 0.2s; + transition: border-color 0.15s, background-color 0.15s, box-shadow 0.15s; position: relative; user-select: none; -webkit-user-select: none; /* Safari */ } .calendar-day:hover { - border-color: #0066cc; - box-shadow: 0 2px 8px rgba(0, 102, 204, 0.1); - background-color: #f9fbff; + border-color: var(--c-accent); + box-shadow: 0 2px 8px rgba(47, 111, 79, 0.12); + background-color: var(--c-accent-weak); } .calendar-day:focus-visible { - outline: 2px solid #0066cc; + outline: 2px solid var(--c-accent); outline-offset: 2px; } .calendar-day--disabled { - background-color: #fafafa; - color: #999; + background-color: var(--c-bg); + color: var(--c-text-faint); cursor: default; } .calendar-day--disabled:hover { - border-color: #e0e0e0; + border-color: var(--c-border); box-shadow: none; - background-color: #fafafa; + background-color: var(--c-bg); } .calendar-day--has-events { - background-color: #fafbff; + background-color: rgba(47, 111, 79, 0.04); } .calendar-day__number { font-weight: 600; - font-size: 14px; - color: #333; - margin-bottom: 4px; + font-size: var(--fs-sm); + color: var(--c-text); + margin-bottom: var(--sp-1); } .calendar-day--disabled .calendar-day__number { - color: #bbb; + color: var(--c-text-faint); } .calendar-day--today .calendar-day__number { - background-color: #0066cc; - color: #fff; + background-color: var(--c-accent); + color: var(--c-surface); width: 24px; height: 24px; border-radius: 50%; display: flex; align-items: center; justify-content: center; - font-size: 12px; + font-size: var(--fs-xs); } .calendar-day--selected { - background-color: #f9fbff; - border-color: #0066cc; - box-shadow: 0 2px 8px rgba(0, 102, 204, 0.1); + background-color: var(--c-accent-weak); + border-color: var(--c-accent); + box-shadow: 0 2px 8px rgba(47, 111, 79, 0.18); } .calendar-day--selected:hover { - background-color: #f0f8ff; + background-color: var(--c-accent-weak); } /* §3.4 — statutory red days / klämdagar shading. Color is never the only signal: each also gets a text badge (see .calendar-day__type-badge) and a title/aria-label carrying the holiday name. */ .calendar-day--red-day { - background-color: #fdeceb; - border-color: #d9776e; + background-color: var(--c-danger-weak); + border-color: var(--c-redday-border); } .calendar-day--red-day:hover { - background-color: #fbdedb; + background-color: var(--c-redday-hover); } .calendar-day--klamdag { - background-color: #fef8e7; - border-color: #d9b23c; + background-color: var(--c-klamdag-bg); + border-color: var(--c-klamdag-border); } .calendar-day--klamdag:hover { - background-color: #fcf0c8; + background-color: var(--c-klamdag-hover); } /* Plain Saturday — neutral grey, deliberately NOT red. Matches the Swedish @@ -208,17 +203,17 @@ an ordinary rest day. See CalendarDay.tsx for the weekday-based split (the backend collapses both into a single "weekend" day_type). */ .calendar-day--weekend { - background-color: #f0f0f0; - border-color: #cfcfcf; + background-color: var(--c-weekend-bg); + border-color: var(--c-weekend-border); } .calendar-day--weekend:hover { - background-color: #e8e8e8; + background-color: var(--c-weekend-hover); } .calendar-day__holiday-name { - font-size: 10px; - color: #a8453d; + font-size: var(--fs-2xs); + color: var(--c-danger); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; @@ -240,7 +235,7 @@ font-size: 9px; font-weight: 700; line-height: 1; - color: #fff; + color: var(--c-surface); background-color: rgba(0, 0, 0, 0.4); border-radius: 3px; padding: 1px 3px; @@ -277,7 +272,7 @@ .calendar-week { display: grid; grid-template-columns: repeat(7, 1fr); - column-gap: 8px; + column-gap: var(--sp-2); row-gap: 3px; } @@ -292,7 +287,10 @@ /* One continuous band segment for this week row. Square by default — .calendar-band--start/--end round only the true first/last row of the whole (possibly multi-week) band, so a period crossing a week boundary - reads as one connected object, not a chain of separate chips. */ + reads as one connected object, not a chain of separate chips. + Border radius here is intentionally NOT --radius-sm: it's asserted + literally (4px/0px) by frontend/e2e/tests/calendar-band.spec.ts, so it + has its own --radius-band token instead of following the shared scale. */ .calendar-band { position: relative; display: flex; @@ -305,17 +303,17 @@ } .calendar-band--start { - border-top-left-radius: 4px; - border-bottom-left-radius: 4px; + border-top-left-radius: var(--radius-band); + border-bottom-left-radius: var(--radius-band); } .calendar-band--end { - border-top-right-radius: 4px; - border-bottom-right-radius: 4px; + border-top-right-radius: var(--radius-band); + border-bottom-right-radius: var(--radius-band); } .calendar-band:focus-visible { - outline: 2px solid #0066cc; + outline: 2px solid var(--c-accent); outline-offset: 1px; } @@ -330,7 +328,7 @@ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); } 30%, 70% { - box-shadow: 0 0 0 3px #ffd54f, 0 1px 2px rgba(0, 0, 0, 0.1); + box-shadow: 0 0 0 3px var(--c-flash), 0 1px 2px rgba(0, 0, 0, 0.1); } } @@ -340,8 +338,8 @@ display: flex; align-items: center; padding: 0 6px; - font-size: 11px; - color: #fff; + font-size: var(--fs-2xs); + color: var(--c-surface); text-shadow: 0 1px 1px rgba(0, 0, 0, 0.35); pointer-events: none; z-index: 1; @@ -393,7 +391,7 @@ } .calendar-event__label { - font-size: 12px; + font-size: var(--fs-2xs); font-weight: 500; overflow: hidden; text-overflow: ellipsis; @@ -416,31 +414,30 @@ } .calendar__legend { - padding: 16px; - background-color: #f9f9f9; - border: 1px solid #e0e0e0; - border-radius: 4px; + padding: var(--sp-4); + background-color: var(--c-bg); + border-radius: var(--radius-sm); } .calendar__legend h3 { - margin: 0 0 12px 0; - font-size: 14px; + margin: 0 0 var(--sp-3) 0; + font-size: var(--fs-sm); font-weight: 600; - color: #333; + color: var(--c-text); } .calendar__legend-items { display: flex; - gap: 24px; + gap: var(--sp-5); flex-wrap: wrap; } .calendar__legend-item { display: flex; align-items: center; - gap: 8px; - font-size: 13px; - color: #666; + gap: var(--sp-2); + font-size: var(--fs-xs); + color: var(--c-text-muted); } .calendar__legend-swatch { @@ -449,32 +446,32 @@ justify-content: center; width: 16px; height: 16px; - border-radius: 2px; + border-radius: 3px; flex-shrink: 0; font-size: 11px; font-weight: bold; - color: #fff; + color: var(--c-surface); } .calendar__legend-swatch--dubbeldag { background-color: rgba(0, 0, 0, 0.2); - border: 1px solid #999; - color: #333; + border: 1px solid var(--c-text-faint); + color: var(--c-text); } .calendar__legend-swatch--red-day { - background-color: #fdeceb; - border: 1px solid #d9776e; + background-color: var(--c-danger-weak); + border: 1px solid var(--c-redday-border); } .calendar__legend-swatch--klamdag { - background-color: #fef8e7; - border: 1px solid #d9b23c; + background-color: var(--c-klamdag-bg); + border: 1px solid var(--c-klamdag-border); } .calendar__legend-swatch--override { - background-color: #fff; - border: 2px dashed #666; + background-color: var(--c-surface); + border: 2px dashed var(--c-text-muted); } /* §Task7 — the calendar becomes the plan: a side rail of presets next to @@ -484,7 +481,7 @@ popover (see PlannedDayModal's "Den här dagen är" switch). */ .calendar__layout { display: flex; - gap: 20px; + gap: var(--sp-5); align-items: flex-start; } @@ -494,22 +491,22 @@ } .calendar__sync-indicator { - font-size: 12px; - color: #666; - margin: 0 0 12px 0; + font-size: var(--fs-xs); + color: var(--c-text-muted); + margin: 0 0 var(--sp-3) 0; } .calendar__toast { display: flex; align-items: center; - gap: 12px; - padding: 10px 14px; - margin-bottom: 16px; - background-color: #eef6ff; - border: 1px solid #b8d9ff; - border-radius: 4px; - font-size: 13px; - color: #003a70; + gap: var(--sp-3); + padding: var(--sp-3) var(--sp-4); + margin-bottom: var(--sp-4); + background-color: var(--c-accent-weak); + border: 1px solid var(--c-accent); + border-radius: var(--radius-sm); + font-size: var(--fs-sm); + color: var(--c-text); } .calendar__toast-message { @@ -517,19 +514,19 @@ } .calendar__toast-undo { - padding: 4px 10px; - border: 1px solid #0066cc; - border-radius: 4px; - background: #fff; - color: #0066cc; + padding: var(--sp-1) var(--sp-3); + border: 1px solid var(--c-accent); + border-radius: var(--radius-sm); + background: var(--c-surface); + color: var(--c-accent); cursor: pointer; - font-size: 12px; + font-size: var(--fs-xs); font-weight: 600; } .calendar__toast-undo:hover:not(:disabled) { - background-color: #0066cc; - color: #fff; + background-color: var(--c-accent); + color: var(--c-surface); } .calendar__toast-undo:disabled { @@ -540,10 +537,10 @@ .calendar__toast-dismiss { background: none; border: none; - font-size: 18px; + font-size: var(--fs-lg); line-height: 1; cursor: pointer; - color: #003a70; + color: var(--c-text-muted); padding: 0 2px; } @@ -555,12 +552,12 @@ display: flex; align-items: center; justify-content: center; - padding: 12px 8px; + padding: var(--sp-3) var(--sp-2); font-weight: 600; - font-size: 12px; - color: #999; - background-color: #fafafa; - border-right: 1px solid #d0d0d0; + font-size: var(--fs-xs); + color: var(--c-text-faint); + background-color: var(--c-bg); + border-right: 1px solid var(--c-border); pointer-events: none; user-select: none; } @@ -569,12 +566,12 @@ display: flex; align-items: center; justify-content: center; - padding: 8px; + padding: var(--sp-2); font-weight: 600; - font-size: 11px; - color: #999; - border-right: 1px solid #d0d0d0; - background-color: #fafafa; + font-size: var(--fs-2xs); + color: var(--c-text-faint); + border-right: 1px solid var(--c-border); + background-color: var(--c-bg); pointer-events: none; user-select: none; min-height: 84px; diff --git a/frontend/src/pages/onboarding.css b/frontend/src/pages/onboarding.css index d108268..3089f5d 100644 --- a/frontend/src/pages/onboarding.css +++ b/frontend/src/pages/onboarding.css @@ -1,21 +1,29 @@ -/* Onboarding wizard (first-run: add child, solo/couple, balances ladder) */ +/* Onboarding wizard (first-run: add child, solo/couple, balances ladder) — + * a step here should breathe: it's a handful of fields at a time, not a + * dense page, so it gets more top/bottom air than the dashboard/settings + * content sections do. */ .onboarding { max-width: 60ch; + margin: 0 auto; + padding-top: var(--sp-6); } .onboarding__indicator { color: var(--c-text-muted); - font-size: var(--fs-sm); - margin-bottom: var(--sp-4); + font-size: var(--fs-xs); + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.04em; + margin-bottom: var(--sp-5); } .onboarding__step h1 { - margin-bottom: var(--sp-2); + margin-bottom: var(--sp-3); } .onboarding__step > p { color: var(--c-text-muted); - margin-bottom: var(--sp-5); + margin-bottom: var(--sp-6); } .onboarding__partner { @@ -41,7 +49,7 @@ border: 1px solid var(--c-border); border-radius: var(--radius); background-color: var(--c-surface); - padding: var(--sp-4); + padding: var(--sp-5); } .onboarding__ladder-item h2 { @@ -51,7 +59,7 @@ .onboarding__ladder-item p { color: var(--c-text-muted); - margin-bottom: var(--sp-3); + margin-bottom: var(--sp-4); } .onboarding__hint { diff --git a/frontend/src/styles/components.css b/frontend/src/styles/components.css index fe7a6ee..6820d57 100644 --- a/frontend/src/styles/components.css +++ b/frontend/src/styles/components.css @@ -20,9 +20,9 @@ align-items: center; justify-content: center; padding: var(--sp-5); - background-color: var(--c-bg); + background-color: var(--c-surface); border-radius: var(--radius); - border: 1px solid var(--c-border); + box-shadow: var(--shadow-sm); } .overview-metrics__value { @@ -157,36 +157,48 @@ color: var(--c-text-muted); } -/* Hero */ +/* Hero — the headline figure. This is the one element on Översikt allowed + * a tinted background; everything below it gives up cards/borders in favor + * of whitespace precisely so this keeps reading as the most important thing + * on the page (see AGENTS.md's "clear hierarchy per screen"). */ .dashboard-hero { - padding: var(--sp-5); + padding: var(--sp-6) var(--sp-6); + margin-bottom: var(--sp-6); border-radius: var(--radius); - border-left: 4px solid var(--c-accent); + border-left: 3px solid var(--c-accent); background-color: var(--c-accent-weak); } .dashboard-hero--warning { - border-left-color: var(--c-warning); + border-left-color: var(--c-danger); background-color: var(--c-danger-weak); } .dashboard-hero__message { margin: 0; - font-size: var(--fs-xl); - font-weight: 600; + font-size: var(--fs-2xl); + font-weight: 700; + line-height: 1.25; + letter-spacing: -0.01em; color: var(--c-text); } /* Shown while headline figures are still the statutory assumption (no FK * fetch or manual entry has happened yet) — see `Dashboard.tsx`. */ +.dashboard__fetch-actions { + display: flex; + gap: var(--sp-3); + flex-wrap: wrap; +} + .dashboard__estimate-badge { display: inline-block; margin: var(--sp-3) 0; padding: var(--sp-1) var(--sp-3); - border-radius: 999px; + border-radius: var(--radius-full); font-size: var(--fs-sm); font-weight: 600; - background-color: #fef8e7; + background-color: var(--c-warning-weak); color: var(--c-warning); cursor: help; } @@ -196,6 +208,16 @@ display: flex; flex-direction: column; gap: var(--sp-4); + margin-bottom: var(--sp-6); +} + +.age4-bars__title { + font-size: var(--fs-base); + font-weight: 600; + color: var(--c-text-muted); + text-transform: uppercase; + letter-spacing: 0.02em; + margin-bottom: 0; } .age4-bars__row { @@ -210,16 +232,17 @@ .age4-bars__track { width: 100%; - height: 20px; - background-color: var(--c-bg); - border-radius: var(--radius); + height: 10px; + background-color: var(--c-surface); + border-radius: var(--radius-full); overflow: hidden; - border: 1px solid var(--c-border); + box-shadow: inset 0 0 0 1px var(--c-border); } .age4-bars__fill { height: 100%; background-color: var(--c-accent); + border-radius: var(--radius-full); transition: width 0.3s ease; } @@ -240,6 +263,16 @@ display: flex; flex-direction: column; gap: var(--sp-3); + margin-bottom: var(--sp-6); +} + +.year-timeline__title { + font-size: var(--fs-base); + font-weight: 600; + color: var(--c-text-muted); + text-transform: uppercase; + letter-spacing: 0.02em; + margin-bottom: var(--sp-1); } .year-timeline__row { @@ -258,10 +291,10 @@ .year-timeline__track { position: relative; flex: 1; - height: 28px; - background-color: var(--c-bg); - border-radius: var(--radius); - border: 1px solid var(--c-border); + height: 24px; + background-color: var(--c-surface); + border-radius: var(--radius-sm); + box-shadow: inset 0 0 0 1px var(--c-border); overflow: hidden; } @@ -269,27 +302,39 @@ position: absolute; top: 0; height: 100%; - min-width: 2px; + min-width: 3px; border: none; cursor: pointer; padding: 0; + transition: opacity 0.15s; +} + +.year-timeline__band:hover { + opacity: 0.85; } -.year-timeline__band:hover, .year-timeline__band:focus-visible { outline: 2px solid var(--c-text); outline-offset: -2px; } -/* Plan Fold (collapsed plan-proposal summary) */ +/* Plan Fold (collapsed plan-proposal summary) — these stay bordered even + * though the plain content sections around them dropped their cards: a + *
disclosure control needs a visible boundary so it reads as + * something to click, not another paragraph of content. */ .plan-fold, .dashboard__more-details { border: 1px solid var(--c-border); border-radius: var(--radius); - padding: var(--sp-4); + padding: var(--sp-4) var(--sp-5); background-color: var(--c-surface); } +.plan-fold + .plan-fold, +.dashboard__more-details + .dashboard__more-details { + margin-top: var(--sp-4); +} + .plan-fold__summary, .dashboard__more-details summary { cursor: pointer; @@ -312,11 +357,11 @@ button { } button:hover:not(:disabled) { - background-color: #1f5a3b; + background-color: var(--c-accent-hover); } button:active:not(:disabled) { - background-color: #0f4a2f; + background-color: var(--c-accent-active); } /* Secondary button */ @@ -418,6 +463,14 @@ button.ghost:hover:not(:disabled) { margin-bottom: var(--sp-4); } +.settings-page__label { + font-size: var(--fs-base); +} + +.settings-page__select { + max-width: 320px; +} + .settings-page__checkbox-label { display: flex; align-items: center; @@ -468,7 +521,7 @@ button.ghost:hover:not(:disabled) { } .settings-page__button:not(:disabled):hover { - background-color: #1f5a3b; + background-color: var(--c-accent-hover); } .settings-page__button:focus-visible { @@ -545,7 +598,7 @@ button.ghost:hover:not(:disabled) { border-radius: 999px; font-size: var(--fs-sm); font-weight: 400; - background-color: #fef8e7; + background-color: var(--c-warning-weak); color: var(--c-warning); cursor: help; } @@ -559,7 +612,7 @@ button.ghost:hover:not(:disabled) { } .vacation-days-table__save:not(:disabled):hover { - background-color: #1f5a3b; + background-color: var(--c-accent-hover); } .vacation-days-table__save:focus-visible { @@ -704,13 +757,13 @@ legend { } .day-overrides-input__status--working { - background-color: #fef3c7; - color: #92400e; + background-color: var(--c-warning-weak); + color: var(--c-warning); } .day-overrides-input__status--off { - background-color: #dbeafe; - color: #1e40af; + background-color: var(--c-success-weak); + color: var(--c-success); } .day-overrides-input__item-note { @@ -918,6 +971,85 @@ legend { font-size: var(--fs-sm); } +/* Tip Card (Optimization Tips, Översikt) — previously had no CSS at all, + * so every tip fell back to a plain `