Files
F-r-ldraledigplannerare/frontend/src/styles/responsive.css
T
jacobh2 0081f60b06 Finish the redesign's edges: auth page, modals, dead code, docs
The sign-in page becomes a plain centred column with the brand mark
and a segmented login/register switch, modals lose their last inner
boxes, the red-days list collapses every year but the current one,
and the retired components' CSS, an unused list component and 23
unreferenced translation keys are removed. ARCHITECTURE.md documents
the Almanacka styling conventions and the per-year balance rule.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XBRLomdLK9jF7wU7a8AA6H
2026-09-20 07:00:22 +02:00

253 lines
5.7 KiB
CSS

/* Responsive & Accessibility
*
* The ONLY file in the app allowed to contain @media (enforced by
* styles.test.ts) — every viewport/motion/contrast override lives here so
* there is one place to check for breakpoints instead of one per feature
* file. */
/* Mobile - Small screens (max 640px) */
@media (max-width: 640px) {
/* Header — the same four groups (brand / nav / identity / tools) reorder
* into three rows: brand + language + logout, the nav as a full-width
* row, then the child select full-width below. 16px gutters. */
.header__bar {
padding: 0 var(--sp-4);
row-gap: var(--sp-2);
padding-bottom: var(--sp-2);
}
.header__brand {
order: 1;
flex: 1 1 auto;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: var(--fs-base);
}
.header__tools {
order: 2;
flex-shrink: 0;
}
.header__nav {
order: 3;
flex: 0 0 100%;
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: var(--sp-1);
}
.header__nav-link {
width: 100%;
justify-content: center;
}
.header__identity {
order: 4;
flex: 0 0 100%;
margin-left: 0;
}
.header__child-select {
width: 100%;
max-width: none;
padding-left: 0;
}
.header__parent-name {
display: none;
}
/* Main content — the container picks up the horizontal gutter; sections
* themselves stay at zero horizontal padding at every width. */
.layout__main {
padding: var(--sp-5) var(--sp-4);
}
.dashboard__section,
.settings-page__section,
.calendar__section,
.tips-page__section {
padding-top: var(--sp-4);
margin-bottom: var(--sp-4);
}
/* Forms - full width */
.form-group input,
.form-group select,
.form-group textarea,
.vacation-days-input__field,
.planning-goal-selector__select,
.parent-profile-input__field {
max-width: none;
}
/* Dashboard — the three key figures tighten; a year row keeps year +
* track on the first line and wraps the per-parent figures under the
* track, each parent on its own line. */
.key-figures {
gap: var(--sp-4) var(--sp-6);
}
.year-row__summary {
grid-template-columns: 3rem minmax(0, 1fr) var(--icon-sm);
}
.year-row__summary::after {
grid-column: 3;
}
.year-row__figures {
grid-row: 2;
grid-column: 2 / -1;
display: flex;
flex-direction: column;
gap: var(--sp-1);
white-space: normal;
}
.year-row__parent {
display: flex;
flex-wrap: wrap;
column-gap: var(--sp-3);
row-gap: 0;
}
.year-row__body {
padding-left: 0;
}
.year-row__table,
.parental-leave-breakdown__table {
width: 100%;
}
/* Modals — the overlay already gutters the content with its own padding
* and `.modal__content`'s `width: min(100%, var(--modal-w))` already caps
* it to the viewport, so only the footer's button stacking needs a
* mobile-specific rule. */
.modal__footer {
flex-direction: column-reverse;
}
.modal__footer .btn {
width: 100%;
}
.qrcode-modal__qr-image {
max-width: 250px;
}
/* Delete-confirmation row: the warning text gets its own line, and the
* two buttons split the row evenly. */
.modal__delete-confirm-text {
width: 100%;
margin-right: 0;
}
.modal__delete-confirm .btn {
flex: 1;
min-width: 0;
}
/* The footer stacks bottom-up here (column-reverse above), so the
* revealed delete scopes sort FIRST to land at the bottom, directly under
* the "Ta bort…" button that opened them. */
.period-popover__delete-group {
order: -10;
}
/* Loading spinner */
.loading-spinner {
padding: var(--sp-5);
min-height: 200px;
}
/* Calendar — cells are ~48px wide at 390px, so the padding tightens and
* the numeral keeps its 24px box flush to the top-left. */
.calendar__weekday {
padding: 0 var(--sp-1) var(--sp-2);
}
.calendar-day {
min-height: 64px;
padding: var(--sp-1) var(--sp-1) calc(var(--sp-1) + var(--band-reserved-height, 0px));
}
.calendar__week-number {
padding-top: var(--sp-1);
}
.calendar-band__label {
padding: 0 var(--sp-1);
}
/* The preset chips scroll sideways in one row instead of wrapping, so the
* grid is never pushed down the page. */
.preset-chips__row {
flex-wrap: nowrap;
overflow-x: auto;
scrollbar-width: none;
padding-bottom: var(--sp-1);
}
.preset-chips__row::-webkit-scrollbar {
display: none;
}
/* Footer — same side gutter as .layout__main at this width. */
.footer__inner {
padding: 0 var(--sp-4);
}
}
/* Tablet - Medium screens (641px to 1024px) */
@media (min-width: 641px) and (max-width: 1024px) {
.layout__main {
padding: var(--sp-6) var(--sp-5);
}
}
/* Accessibility - Reduced Motion
*
* The universal `*` rule collapses every transition/animation duration to
* near-zero already; there is deliberately no second, narrower list of
* selectors repeating that on top of it. */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
/* High Contrast Mode — the darkest step of the accent, pure black text and
* a visibly darker hairline. */
@media (prefers-contrast: more) {
:root {
--c-text: #000;
--c-text-muted: #333;
--c-accent: #1d3ca3;
--c-border: #555;
}
button:focus-visible,
a:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible {
outline-width: 3px;
}
}
/* Keyboard Navigation */
:focus-visible {
outline: 2px solid var(--c-accent);
outline-offset: 2px;
}