Settle on one visual language
The interface had drifted into two of everything. The calendar and its popovers were built around a blue accent the rest of the app had long since replaced with green, five different sizes served the same class of small label, and page sections were a stack of identical bordered cards that gave no sense of what mattered most. Sections now separate by space and a hairline, the overview leads with its headline figure at a size that says so, and the palette, type scale and spacing live in one place as variables rather than scattered literals. Two things were not styled at all. The sign-in screen was an unstyled form in the top-left corner, and the tip cards had no rules whatsoever despite their markup already carrying a severity, so every tip rendered identically with a plain green square for its dismiss control. The calendar was also unusable on a narrow screen: the preset rail never collapsed and covered the last three columns of the grid.
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 `<details>` 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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
+37
-29
@@ -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 (
|
||||
<main>
|
||||
<h1>{isRegister ? t('auth.createAccountTitle') : t('auth.loginTitle')}</h1>
|
||||
<form onSubmit={(e) => void handleSubmit(e)}>
|
||||
{isRegister && (
|
||||
<main className="auth-page">
|
||||
<div className="auth-page__card">
|
||||
<p className="auth-page__brand">{t('app.name')}</p>
|
||||
<h1 className="auth-page__title">{isRegister ? t('auth.createAccountTitle') : t('auth.loginTitle')}</h1>
|
||||
<form className="auth-page__form" onSubmit={(e) => void handleSubmit(e)}>
|
||||
{isRegister && (
|
||||
<input
|
||||
placeholder={t('auth.namePlaceholder')}
|
||||
value={displayName}
|
||||
onChange={(e) => setDisplayName(e.target.value)}
|
||||
/>
|
||||
)}
|
||||
<input
|
||||
placeholder={t('auth.namePlaceholder')}
|
||||
value={displayName}
|
||||
onChange={(e) => setDisplayName(e.target.value)}
|
||||
type="email"
|
||||
data-testid="auth-email"
|
||||
placeholder={t('auth.emailPlaceholder')}
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
/>
|
||||
)}
|
||||
<input
|
||||
type="email"
|
||||
data-testid="auth-email"
|
||||
placeholder={t('auth.emailPlaceholder')}
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
/>
|
||||
<input
|
||||
type="password"
|
||||
data-testid="auth-password"
|
||||
placeholder={t('auth.passwordPlaceholder')}
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
/>
|
||||
{error && <p role="alert">{error}</p>}
|
||||
<button type="submit" data-testid="auth-submit">
|
||||
{isRegister ? t('auth.registerButton') : t('auth.loginButton')}
|
||||
<input
|
||||
type="password"
|
||||
data-testid="auth-password"
|
||||
placeholder={t('auth.passwordPlaceholder')}
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
/>
|
||||
{error && (
|
||||
<p role="alert" className="auth-page__error">
|
||||
{error}
|
||||
</p>
|
||||
)}
|
||||
<button type="submit" data-testid="auth-submit">
|
||||
{isRegister ? t('auth.registerButton') : t('auth.loginButton')}
|
||||
</button>
|
||||
</form>
|
||||
<button type="button" className="auth-page__switch button--ghost" onClick={() => setIsRegister((v) => !v)}>
|
||||
{isRegister ? t('auth.haveAccountButton') : t('auth.createAccountButton')}
|
||||
</button>
|
||||
</form>
|
||||
<button type="button" onClick={() => setIsRegister((v) => !v)}>
|
||||
{isRegister ? t('auth.haveAccountButton') : t('auth.createAccountButton')}
|
||||
</button>
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -229,23 +229,17 @@ export function Dashboard(): ReactNode {
|
||||
|
||||
{age4Bars.length > 0 && <Age4Bars bars={age4Bars} />}
|
||||
|
||||
<section className="dashboard__section">
|
||||
<div style={{ display: 'flex', gap: '10px' }}>
|
||||
<button
|
||||
onClick={handleFetchFK}
|
||||
disabled={!selectedChildId}
|
||||
style={{ padding: '0.5rem 1rem', cursor: selectedChildId ? 'pointer' : 'not-allowed' }}
|
||||
>
|
||||
{t('dashboard.fetchFromFk')}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setShowManualFetchModal(true)}
|
||||
disabled={!selectedChildId}
|
||||
style={{ padding: '0.5rem 1rem', cursor: selectedChildId ? 'pointer' : 'not-allowed' }}
|
||||
>
|
||||
{t('dashboard.enterManually')}
|
||||
</button>
|
||||
</div>
|
||||
<section className="dashboard__section dashboard__fetch-actions">
|
||||
<button onClick={handleFetchFK} disabled={!selectedChildId}>
|
||||
{t('dashboard.fetchFromFk')}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setShowManualFetchModal(true)}
|
||||
disabled={!selectedChildId}
|
||||
className="button--secondary"
|
||||
>
|
||||
{t('dashboard.enterManually')}
|
||||
</button>
|
||||
</section>
|
||||
|
||||
{selectedChild && (
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
/* Login/register — the app's front door, rendered outside <Layout> (no
|
||||
* header yet, there's no session). Previously a bare, unstyled <main> 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%;
|
||||
}
|
||||
+137
-140
@@ -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,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 8"><path d="M1 1l5 5 5-5" stroke="%23333" stroke-width="1.5" fill="none" stroke-linecap="round"/></svg>');
|
||||
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 8"><path d="M1 1l5 5 5-5" stroke="%231c2733" stroke-width="1.5" fill="none" stroke-linecap="round"/></svg>');
|
||||
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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
* <details> 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 `<button>` for its dismiss control
|
||||
* (a solid accent-green square) and no visual grouping. The severity
|
||||
* modifier classes already existed on the markup; only the color-coding
|
||||
* they imply was missing. */
|
||||
.tip-card {
|
||||
padding: var(--sp-4);
|
||||
border-left: 3px solid var(--c-border);
|
||||
background-color: var(--c-surface);
|
||||
border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
|
||||
}
|
||||
|
||||
.tip-card + .tip-card {
|
||||
margin-top: var(--sp-3);
|
||||
}
|
||||
|
||||
.tip-card--info {
|
||||
border-left-color: var(--c-text-faint);
|
||||
}
|
||||
|
||||
.tip-card--warning {
|
||||
border-left-color: var(--c-warning);
|
||||
background-color: var(--c-warning-weak);
|
||||
}
|
||||
|
||||
.tip-card--action {
|
||||
border-left-color: var(--c-accent);
|
||||
background-color: var(--c-accent-weak);
|
||||
}
|
||||
|
||||
.tip-card__header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: var(--sp-3);
|
||||
margin-bottom: var(--sp-1);
|
||||
}
|
||||
|
||||
.tip-card__title {
|
||||
margin: 0;
|
||||
font-size: var(--fs-base);
|
||||
font-weight: 600;
|
||||
color: var(--c-text);
|
||||
}
|
||||
|
||||
.tip-card__dismiss {
|
||||
flex-shrink: 0;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: transparent;
|
||||
color: var(--c-text-muted);
|
||||
border: none;
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: var(--fs-base);
|
||||
line-height: 1;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.15s, color 0.15s;
|
||||
}
|
||||
|
||||
.tip-card__dismiss:hover {
|
||||
background-color: rgba(0, 0, 0, 0.06);
|
||||
color: var(--c-text);
|
||||
}
|
||||
|
||||
.tip-card__dismiss:focus-visible {
|
||||
outline: 2px solid var(--c-accent);
|
||||
outline-offset: 1px;
|
||||
}
|
||||
|
||||
.tip-card__body {
|
||||
margin: 0;
|
||||
font-size: var(--fs-sm);
|
||||
color: var(--c-text-muted);
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 640px) {
|
||||
[data-testid='metrics-list'] {
|
||||
|
||||
@@ -3,7 +3,16 @@
|
||||
@import './modals.css';
|
||||
@import './responsive.css';
|
||||
|
||||
/* Design Tokens */
|
||||
/* Design Tokens
|
||||
*
|
||||
* One neutral ground + one accent; every other colour is semantic (danger/
|
||||
* warning/success) and exists as a solid + "-weak" tint pair. Everything
|
||||
* below is used app-wide, including the calendar and modal families that
|
||||
* used to hardcode their own hex values — see calendar.css/period-popover.css/
|
||||
* etc. Two values are load-bearing for e2e: --c-danger-weak (red day
|
||||
* background) and --c-weekend-bg (plain-Saturday background) — read
|
||||
* directly by frontend/e2e/tests/red-days.spec.ts. Do not repurpose either.
|
||||
*/
|
||||
:root {
|
||||
/* Color - calm, trustworthy palette */
|
||||
--c-bg: #f7f8fa;
|
||||
@@ -11,13 +20,37 @@
|
||||
--c-border: #e3e6eb;
|
||||
--c-text: #1c2733;
|
||||
--c-text-muted: #5b6675;
|
||||
--c-accent: #2f6f4f; /* calm green */
|
||||
--c-text-faint: #94a0ac;
|
||||
--c-accent: #2f6f4f; /* calm green — the app's one accent */
|
||||
--c-accent-weak: #e8f1ec;
|
||||
--c-accent-hover: #1f5a3b;
|
||||
--c-accent-active: #0f4a2f;
|
||||
--c-danger: #b3261e;
|
||||
--c-danger-weak: #fdeceb;
|
||||
--c-danger-weak: #fdeceb; /* also the red-day cell background — see note above */
|
||||
--c-danger-hover: #8a1a15;
|
||||
--c-success: #1e7d4f;
|
||||
--c-success-weak: #e7f4ec;
|
||||
--c-success-hover: #0d6a37;
|
||||
--c-warning: #8a6d1f;
|
||||
--c-warning-weak: #fef8e7;
|
||||
|
||||
/* Calendar-specific semantics (klämdag/weekend/red-day borders + hover) —
|
||||
* kept distinct from the generic danger/warning pair above because a red
|
||||
* day and a klämdag are both visible on screen at once and need to read
|
||||
* as different hues, not different intensities of the same one. */
|
||||
--c-weekend-bg: #f0f0f0; /* plain Saturday — read directly by red-days.spec.ts */
|
||||
--c-weekend-border: #cfcfcf;
|
||||
--c-weekend-hover: #e8e8e8;
|
||||
--c-redday-border: #d9776e;
|
||||
--c-redday-hover: #fbdedb;
|
||||
--c-klamdag-bg: #fef8e7;
|
||||
--c-klamdag-border: #d9b23c;
|
||||
--c-klamdag-hover: #fcf0c8;
|
||||
/* The one deliberately loud color in the app — a "this just changed"
|
||||
* flash ring on a calendar band, never a static/resting state. */
|
||||
--c-flash: #ffd54f;
|
||||
|
||||
--overlay: rgba(16, 24, 40, 0.5);
|
||||
|
||||
/* Spacing scale (8px base) */
|
||||
--sp-1: 4px;
|
||||
@@ -29,6 +62,8 @@
|
||||
--sp-8: 48px;
|
||||
|
||||
/* Type scale */
|
||||
--fs-2xs: 0.6875rem;
|
||||
--fs-xs: 0.75rem;
|
||||
--fs-sm: 0.875rem;
|
||||
--fs-base: 1rem;
|
||||
--fs-lg: 1.25rem;
|
||||
@@ -37,7 +72,13 @@
|
||||
--lh: 1.5;
|
||||
|
||||
/* Shape & depth */
|
||||
--radius-sm: 6px;
|
||||
--radius: 8px;
|
||||
--radius-full: 999px;
|
||||
/* Matches frontend/e2e/tests/calendar-band.spec.ts's literal 4px/0px
|
||||
* assertions on band corners — kept as its own token (not tied to
|
||||
* --radius-sm) so a future radius change there can't silently break it. */
|
||||
--radius-band: 4px;
|
||||
--shadow-sm: 0 1px 2px rgba(16, 24, 40, 0.06);
|
||||
--shadow-md: 0 4px 12px rgba(16, 24, 40, 0.08);
|
||||
|
||||
@@ -148,22 +189,27 @@ body {
|
||||
font-size: var(--fs-base);
|
||||
}
|
||||
|
||||
/* Headings */
|
||||
/* Headings — tighter leading than body text, the usual editorial cue that a
|
||||
* line is a heading rather than another sentence of the paragraph below it. */
|
||||
h1 {
|
||||
font-size: var(--fs-2xl);
|
||||
font-weight: 700;
|
||||
line-height: 1.2;
|
||||
letter-spacing: -0.01em;
|
||||
margin-bottom: var(--sp-4);
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: var(--fs-xl);
|
||||
font-weight: 700;
|
||||
line-height: 1.25;
|
||||
margin-bottom: var(--sp-3);
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: var(--fs-lg);
|
||||
font-weight: 600;
|
||||
line-height: 1.3;
|
||||
margin-bottom: var(--sp-3);
|
||||
}
|
||||
|
||||
@@ -250,7 +296,7 @@ textarea:focus {
|
||||
input:disabled,
|
||||
select:disabled,
|
||||
textarea:disabled {
|
||||
background-color: #f0f0f0;
|
||||
background-color: var(--c-bg);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
@@ -323,7 +369,7 @@ main {
|
||||
|
||||
.warning-message {
|
||||
padding: var(--sp-3) var(--sp-4);
|
||||
background-color: #fef8e7;
|
||||
background-color: var(--c-warning-weak);
|
||||
color: var(--c-warning);
|
||||
border-left: 4px solid var(--c-warning);
|
||||
border-radius: var(--radius);
|
||||
|
||||
@@ -119,7 +119,7 @@
|
||||
}
|
||||
|
||||
.qrcode-modal__status--pending {
|
||||
background-color: #fef8e7;
|
||||
background-color: var(--c-warning-weak);
|
||||
color: var(--c-warning);
|
||||
}
|
||||
|
||||
|
||||
@@ -155,6 +155,18 @@
|
||||
.calendar-day__number {
|
||||
font-size: var(--fs-sm);
|
||||
}
|
||||
|
||||
/* Pre-existing bug this pass also fixes: `.calendar__layout` never
|
||||
* stacked, so the fixed-width preset rail sat on top of Friday/Saturday/
|
||||
* Sunday's columns instead of beside them once the month grid had no room
|
||||
* left. The rail moves below the grid and goes full-width. */
|
||||
.calendar__layout {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.preset-rail {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
/* Tablet - Medium screens (641px to 1024px) */
|
||||
|
||||
Reference in New Issue
Block a user