Eerste commit

This commit is contained in:
jf
2026-09-27 14:21:31 +00:00
commit de4ff128cb
295 changed files with 47374 additions and 0 deletions
+50
View File
@@ -0,0 +1,50 @@
// Laat de prijs meelopen terwijl een adverteerder datums en aantallen invult.
//
// Waarom dit een apart bestand is en niet een <script> in de pagina zelf: die pagina wordt
// statisch weergegeven, en Blazor vervangt bij het navigeren alleen de HTML. Een <script> dat
// op die manier binnenkomt wordt NIET uitgevoerd. Het gevolg was een prijs die niets deed zodra
// je via een link op het boekscherm kwam - en wel werkte als je de pagina zelf ververste, wat
// het extra verwarrend maakt. Dit bestand staat in de kop van elke pagina en is er dus altijd.
//
// Wat hier wordt uitgerekend is een gemak, geen bedrag. De server rekent bij het indienen zelf
// uit wat het kost; wie met deze pagina knoeit, verandert alleen wat hij zelf ziet staan.
window.mbPrijs = function () {
var vak = document.getElementById('prijsvak');
var vanaf = document.getElementById('vanaf');
var tot = document.getElementById('tot');
var perUur = document.getElementById('peruur');
var prijs = document.getElementById('prijs');
var som = document.getElementById('prijssom');
if (!vak || !vanaf || !tot || !perUur || !prijs) return;
var tarief = parseInt(vak.dataset.tarief, 10);
var bodem = parseInt(vak.dataset.bodem, 10);
var max = parseInt(vak.dataset.max, 10);
var van = Date.parse(vanaf.value);
var totdat = Date.parse(tot.value);
var keer = parseInt(perUur.value, 10);
function schrijf(bedrag, uitleg) {
prijs.textContent = bedrag;
if (som) som.textContent = uitleg;
}
if (isNaN(van) || isNaN(totdat) || isNaN(keer)) return schrijf('—', '');
if (totdat < van) return schrijf('—', 'de einddatum ligt voor de begindatum');
if (keer < 1) return schrijf('—', 'vul in hoe vaak per uur');
if (keer > max) return schrijf('—', 'hoogstens ' + max + ' keer per uur');
var dagen = Math.round((totdat - van) / 86400000) + 1;
var centen = Math.max(dagen * keer * tarief, bodem);
schrijf(euro(centen),
'(' + dagen + ' dagen × ' + keer + ' per uur × ' + tarief + ' cent, ' +
'met een minimum van ' + euro(bodem) + ')');
};
// Zelfde vorm als op de server: een komma, want hier leest een mens het.
function euro(centen) {
return '€ ' + (centen / 100).toFixed(2).replace('.', ',');
}
+577
View File
@@ -0,0 +1,577 @@
/* MijnBesluit - rustig, warm en groot genoeg om makkelijk te bedienen.
Het onderwerp is zwaar; het scherm hoeft dat niet ook te zijn. */
:root {
--achtergrond: #faf7f2;
--vlak: #ffffff;
--vlak-zacht: #f3efe8;
--inkt: #2a2724;
--inkt-zacht: #6b645c;
/* Huisstijl: het groen uit het logo van mijnbesluit.nl. */
--merk: #C1D13F;
--merk-donker: #AFC02F;
/* Op dat groen hoort donkere tekst; wit is er nauwelijks op te lezen. */
--merk-tekst: #2a2724;
/* Donkerder groen voor tekst en randen, want het logogroen is te licht om op te lezen. */
--accent: #6B7A1C;
--accent-donker: #556115;
--accent-zacht: #F1F5DC;
--rand: #e6e0d7;
--waarschuwing: #8a5a22;
--waarschuwing-zacht: #fbf1e3;
--fout: #9b3b32;
--straal: 14px;
--schaduw: 0 1px 2px rgba(42, 39, 36, .05), 0 6px 20px rgba(42, 39, 36, .06);
/* De hoogte van de bovenbalk op de site. Staat hier omdat de navigatie er precies
onder moet blijven plakken; twee losse getallen lopen vroeg of laat uit elkaar.
Hoog genoeg voor de uitlogknop, die zelf 36px is. */
--kop-hoogte: 66px;
}
@media (prefers-color-scheme: dark) {
:root {
--achtergrond: #1b1917;
--vlak: #242120;
--vlak-zacht: #2c2927;
--inkt: #f0ece6;
--inkt-zacht: #a8a099;
--merk: #C1D13F;
--merk-donker: #CEDD5B;
--merk-tekst: #1f1d1a;
--accent: #C1D13F;
--accent-donker: #D2E062;
--accent-zacht: #2b3017;
--rand: #38342f;
--waarschuwing: #d9a55f;
--waarschuwing-zacht: #33291a;
--fout: #e08e84;
--schaduw: 0 1px 2px rgba(0, 0, 0, .3), 0 6px 20px rgba(0, 0, 0, .25);
}
}
* { box-sizing: border-box; }
html { font-size: 17px; }
body {
margin: 0;
background: var(--achtergrond);
color: var(--inkt);
font-family: "Segoe UI", system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif;
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
h1, h2, h3 { line-height: 1.25; font-weight: 600; letter-spacing: -.01em; margin: 0 0 .4em; }
h1 { font-size: 1.9rem; }
h2 { font-size: 1.35rem; }
h3 { font-size: 1.1rem; }
p { margin: 0 0 1em; }
a { color: var(--accent-donker); }
/* Een <button> die eruit moet zien als een link (bijv. omdat hij naar een popup opent in
plaats van naar een andere pagina) - anders krijgt hij de standaard knoprand/achtergrond
van de browser mee. */
/* Een <button> die eruit moet zien als de "?"-uitleg-regel (zie .uitklap hieronder),
omdat hij net als die een popup opent in plaats van naar een andere pagina te gaan. */
.alsLink {
background: none; border: none; padding: 0; margin: .3rem 0; display: inline-flex;
align-items: center; gap: .4rem; cursor: pointer;
color: var(--accent-donker); font: inherit; font-size: .9rem;
}
.alsLink::before {
content: "?"; width: 20px; height: 20px; border-radius: 50%;
background: var(--accent-zacht); color: var(--accent-donker);
display: inline-flex; align-items: center; justify-content: center;
font-weight: 700; font-size: .78rem; flex: none;
}
/* ------------------------------------------------------------------ opbouw */
.schil { display: flex; flex-direction: column; min-height: 100vh; }
.kop {
display: flex; align-items: center; gap: 1rem;
padding: .9rem 1.25rem;
background: var(--vlak);
border-bottom: 1px solid var(--rand);
position: sticky; top: 0; z-index: 20;
}
/* Op de site een vaste hoogte, zodat de navigatie er precies onder kan blijven staan.
In de app niet: daar zit een terugknop in de balk die zijn eigen ruimte nodig heeft. */
.schil:not(.app) .kop { box-sizing: border-box; height: var(--kop-hoogte); }
/* In de app staan de balken echt vast: de inhoud schuift eronderdoor. Sticky is niet genoeg
zodra er binnen een scherm wordt gescrold. */
.schil.app { min-height: 100dvh; }
/* De bovenbalk staat vast op top:0, en dat is onder de statusbalk van de telefoon - de klok,
het bereik en het accupercentage. Door viewport-fit=cover loopt de webview daar tot onderaan
doorheen, dus moet de balk zelf die ruimte vrijhouden. De inhoud deed dat al wel; de balk
niet, en daardoor liep "Mijn mensen" en "Uitloggen" door de tijd en het batterijsymbool heen. */
.schil.app .kop.vast {
position: fixed; top: 0; left: 0; right: 0;
padding-top: calc(.9rem + env(safe-area-inset-top));
}
.schil.app .nav.vast { position: fixed; bottom: 0; left: 0; right: 0; }
.schil.app .inhoud {
/* Ruimte voor de twee balken, plus wat de telefoon zelf bovenaan en onderaan inneemt. */
padding-top: calc(4.5rem + env(safe-area-inset-top));
padding-bottom: calc(5.5rem + env(safe-area-inset-bottom));
}
/* De terugknop in de bovenbalk. */
.terug {
border: none; background: transparent; cursor: pointer;
font-size: 1.8rem; line-height: 1; color: var(--inkt-zacht);
padding: 0 .2rem 0 0; margin: 0;
}
.kop .merk {
font-weight: 600; font-size: 1.05rem; color: var(--inkt);
text-decoration: none; display: flex; align-items: center; gap: .55rem;
}
/* Het logobestand is 512x512 voor scherp gebruik elders; zonder deze regel toont de browser
het op ware grootte en overspoelt het de hele kopbalk (en de rest van de pagina). */
.kop .merk img { height: 28px; width: auto; flex: none; }
.kop .merk .stip {
width: 26px; height: 26px; border-radius: 7px; flex: none;
background: var(--merk); color: #fff;
display: flex; align-items: center; justify-content: center;
font-size: .72rem; font-weight: 700; letter-spacing: -.02em;
}
.kop .rechts { margin-left: auto; display: flex; align-items: center; gap: .9rem; font-size: .92rem; }
/* Op een telefoon duwt het logo boven het inlogscherm de rest een heel stuk naar beneden -
op dat scherm staat er toch nog niets anders in de balk dan "Inloggen". Op een groter
scherm is daar ruimte genoeg voor en blijft het logo gewoon staan. Zie het "inlog-scherm"
-merkteken op Inloggen.razor. */
@media (max-width: 480px) {
.schil:has(.inlog-scherm) .kop .merk { display: none; }
}
.inhoud { flex: 1; width: 100%; max-width: 900px; margin: 0 auto; padding: 1.6rem 1.25rem 5.5rem; }
/* Navigatie: onderaan op de telefoon, bovenaan op een groot scherm. */
.nav {
position: fixed; bottom: 0; left: 0; right: 0; z-index: 20;
display: flex; background: var(--vlak);
border-top: 1px solid var(--rand);
padding: .35rem .25rem calc(.35rem + env(safe-area-inset-bottom));
}
.nav a {
flex: 1; text-align: center; text-decoration: none;
color: var(--inkt-zacht); font-size: .72rem;
padding: .45rem .2rem; border-radius: 10px;
display: flex; flex-direction: column; align-items: center; gap: .2rem;
}
.nav a .teken { font-size: 1.2rem; line-height: 1; }
.nav a.actief { color: var(--accent-donker); background: var(--accent-zacht); }
@media (min-width: 720px) {
html { font-size: 16px; }
.nav {
/* Vastzetten net onder de bovenbalk: samen vormen ze een balk die blijft staan. */
position: sticky; top: var(--kop-hoogte); z-index: 19;
border-top: none; border-bottom: 1px solid var(--rand);
justify-content: center; gap: .3rem; padding: .3rem 1rem;
}
.nav a { flex: none; flex-direction: row; font-size: .92rem; padding: .5rem .9rem; }
.nav a .teken { font-size: 1rem; }
.inhoud { padding-bottom: 3rem; }
}
/* ------------------------------------------------------------------ blokken */
.kaart {
background: var(--vlak);
border: 1px solid var(--rand);
border-radius: var(--straal);
padding: 1.25rem;
box-shadow: var(--schaduw);
margin-bottom: 1rem;
}
/* Een compacte kaart: voor lijsten waar je er veel van onder elkaar ziet. */
.kaart.compact { padding: .9rem 1rem; }
.kaart.klikbaar { display: block; text-decoration: none; color: inherit; }
.kaart.klikbaar:hover { border-color: var(--accent); }
/* Je eigen kaart bovenaan Mijn mensen. Hij hoort er wel bij te horen en er toch net uit te
springen: een randje in de accentkleur, want dit is de enige kaart die ergens heen gaat. */
.kaart.zelf { border-color: var(--accent); }
/* Twee kleine knoppen naast elkaar op een tegel, met minder ruimte ertussen dan een gewone rij.
Hier gemeten: met "Wijzigen" en "Terugtrekken" voluit hield het onderwerp op een telefoon van
360 pixels nog 54 pixels over - "Ee...". Als tekentjes, met deze kleine tussenruimte, is dat
133. Daarom staan er hier iconen en geen woorden. */
.tegelknoppen { display: flex; gap: .35rem; flex: none; }
.tegelknoppen .knop { padding-left: .6rem; padding-right: .6rem; }
/* Een bericht dat nog niet gelezen is. Het randje in de accentkleur vervangt het labeltje
"Nieuw" dat eerst naast de knop stond: dat kostte breedte die het onderwerp harder nodig had. */
.kaart.nieuw { border-color: var(--accent); }
/* Een tegel met knoppen rechtsonder in de hoek, in plaats van naast de tekst.
Zo loopt het onderwerp door tot het eind van de tegel, boven de knoppen langs, en houdt het
op een telefoon van 360 pixels 222 pixels over in plaats van 133. Alleen de onderste regel
maakt ruimte vrij voor de knoppen; die staat er precies naast.
De hoogte blijft gelijk: de knoppen zweven over de ruimte die er toch al was. */
.kaart.tegel { position: relative; }
.kaart.tegel .tegelknoppen { position: absolute; right: 1rem; bottom: .85rem; }
.kaart.tegel .rij.persoon > .groei > .zacht:last-child { padding-right: 6.2rem; }
/* Breekt af naar een volgende regel zodra het niet meer past. Zonder flex-wrap lopen drie
knoppen naast elkaar het scherm van een telefoon uit, en is de laatste niet te bereiken. */
.rij { display: flex; align-items: center; gap: .9rem; flex-wrap: wrap; }
.rij .groei { flex: 1; min-width: 0; }
/* Een kop in zo'n rij mag krimpen, maar zijn tekst hoort dan af te breken in plaats van over
de knop ernaast heen te lopen. Dat gebeurde op de telefoon bij Momenten en Mijn kluis. */
.rij > h1, .rij > h2, .rij > h3 { overflow-wrap: anywhere; }
/* De rij met een persoon erin: penning, naam, en rechts de status.
Deze rij breekt bewust NIET af. De status hoort altijd op dezelfde plek te staan - rechtsboven
op de tegel - zodat je hem in een lijst met één blik afleest. Zakt hij bij de een wel en bij
de ander niet naar een eigen regel, dan moet je elke kaart apart bekijken.
De naam krijgt de ruimte die overblijft en wordt afgekapt als het niet past. Dat is de goede
afweging: je herkent iemand aan het begin van zijn naam, en de status is een vaste ankerplek. */
.rij.persoon { flex-wrap: nowrap; }
.rij.persoon > .groei { min-width: 0; }
/* De regels onder de naam op een persoonsrij: naam van de ander, datum. Net als de naam zelf
worden ze afgekapt in plaats van afgebroken. Zonder dit groeide een tegel bij Momenten naar
vijf regels zodra er een lange bezorgtekst onder stond, en dan staat de rest van de lijst
buiten beeld. */
.rij.persoon > .groei > .zacht {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* De regel onder de naam: "Partner (59)" links, de status rechts.
De status stond eerst naast de naam, maar dan houdt die op een telefoon zo weinig over dat
zelfs "Karin Ophuis" werd afgekapt. Hier gemeten: 115 pixels naast de naam tegen 242 op deze
regel. De status staat nog steeds altijd op dezelfde plek rechts op de tegel - alleen een
regel lager - en de naam krijgt de volle breedte. */
.onderregel { display: flex; align-items: center; gap: .6rem; }
.onderregel > .zacht { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.onderregel > .label { flex: none; }
/* Een naam die niet past wordt afgekapt met puntjes, en breekt niet over drie regels. Een
kaart van drie regels hoog zet de rest van de lijst buiten beeld, en je herkent iemand
toch aan het begin van zijn naam. De volledige naam staat in het wijzigscherm, en op een
muis verschijnt hij als zwevende tekst. */
.rij.persoon .groot {
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* Knoppen mogen krimpen tot hun tekst, maar nooit onleesbaar smal worden. */
.rij > .knop { flex: 0 1 auto; }
.stapel { display: flex; flex-direction: column; gap: .75rem; }
.zacht { color: var(--inkt-zacht); font-size: .9rem; }
.groot { font-size: 1.05rem; font-weight: 600; }
/* Het aantal achter de titel van een tegel, in plaats van op een regel eronder. Zo blijven
de tegels laag en passen er meer op een scherm. */
.aantal {
display: inline-block; min-width: 1.9rem; text-align: center;
margin-left: .4rem; padding: .05rem .5rem;
border-radius: 999px; background: var(--vlak-zacht); color: var(--inkt-zacht);
font-size: .85rem; font-weight: 600; vertical-align: middle;
}
.aantal.nieuw { background: var(--merk); color: var(--merk-tekst); }
/* Wat er speelt, boven de tegels. */
.speelt { margin-bottom: 1.2rem; }
.speelt .regel {
display: flex; align-items: flex-start; gap: .8rem;
padding: .8rem 1rem;
background: var(--vlak); border: 1px solid var(--rand); border-radius: var(--straal);
}
.speelt .regel.let-op { border-color: var(--waarschuwing); background: var(--waarschuwing-zacht); }
.speelt .regel .teken { font-size: 1.2rem; line-height: 1.4; }
.raster { display: grid; gap: 1rem; grid-template-columns: 1fr; }
@media (min-width: 620px) { .raster.twee { grid-template-columns: 1fr 1fr; } }
/* ------------------------------------------------------------------ knoppen */
.knop {
display: inline-flex; align-items: center; justify-content: center; gap: .5rem;
min-height: 48px; padding: .7rem 1.3rem;
border: 1px solid transparent; border-radius: 10px;
background: var(--merk); color: var(--merk-tekst);
font: inherit; font-weight: 600; cursor: pointer; text-decoration: none;
}
/* Een knop mag nooit breder worden dan zijn omgeving; de tekst breekt dan maar af. */
.knop { max-width: 100%; }
@media (max-width: 420px) {
.knop { padding-left: .9rem; padding-right: .9rem; }
}
.knop:hover { background: var(--merk-donker); }
.knop:disabled { opacity: .55; cursor: default; }
.knop.vol { width: 100%; }
/* Kleine knop voor in een rij, zoals bij een familielid. Nog steeds groot genoeg om met een
duim te raken: 40 pixels is de ondergrens die je wilt aanhouden. */
.knop.klein { min-height: 40px; padding: .35rem .8rem; font-size: .9rem; font-weight: 500; }
.knop.rustig { background: transparent; color: var(--accent-donker); border-color: var(--rand); }
.knop.rustig:hover { border-color: var(--accent); background: var(--accent-zacht); }
.knop.gevaar { background: transparent; color: var(--fout); border-color: var(--rand); }
/* ------------------------------------------------------------------ formulieren */
label { display: block; font-weight: 500; margin-bottom: .3rem; font-size: .93rem; }
/* Alle invoervelden, ook die zonder expliciete type: een <input> zonder type is een
tekstveld, maar matcht input[type=text] niet. Zonder deze regel valt zo'n veld terug op
zijn standaardbreedte en loopt het op een telefoon buiten het scherm. */
input:not([type=checkbox]):not([type=radio]):not([type=file]):not([type=submit]),
select, textarea {
width: 100%; max-width: 100%; min-height: 48px;
padding: .65rem .8rem;
border: 1px solid var(--rand); border-radius: 10px;
background: var(--vlak); color: var(--inkt);
font: inherit;
}
textarea { min-height: 130px; resize: vertical; line-height: 1.55; }
textarea[data-meegroeit] { min-height: unset; overflow: hidden; resize: none; }
input:focus, select:focus, textarea:focus {
outline: none; border-color: var(--accent);
box-shadow: 0 0 0 3px var(--accent-zacht);
}
.veld { margin-bottom: 1rem; }
/* Een veld dat niet goed is ingevuld: springNaarVeld() in validatie.js zet deze klasse erop,
scrollt het in beeld en geeft het de focus. Verdwijnt vanzelf zodra iemand er iets aan
verandert - zie validatie.js. */
.veld-fout { border-color: var(--fout) !important; box-shadow: 0 0 0 3px rgba(155, 59, 50, .15); }
/* Drie keuzelijsten naast elkaar voor een datum. Dag en jaar krijgen een vaste breedte, want
met 'flex' erop kromp het jaar op een telefoon tot "196" - het laatste cijfer viel weg.
De maand neemt wat er overblijft. */
.datumkiezer { display: flex; gap: .4rem; align-items: center; }
.datumkiezer select { min-width: 0; padding-left: .5rem; padding-right: .3rem; }
.datumkiezer .dag { flex: 0 0 4.2rem; }
.datumkiezer .maand { flex: 1 1 5rem; }
.datumkiezer .jaar { flex: 0 0 5.4rem; }
.veld .uitleg { font-size: .85rem; color: var(--inkt-zacht); margin-top: .3rem; }
.keuze { display: flex; align-items: flex-start; gap: .6rem; margin-bottom: .6rem; }
.keuze input { width: 20px; height: 20px; min-height: 0; margin-top: .25rem; flex: none; }
.keuze label { margin: 0; font-weight: 400; }
/* De geschiedenis van een profiel, onderaan het wijzigscherm: wanneer aangemaakt,
wanneer uitgenodigd, wanneer overgenomen. */
.geschiedenis { border-top: 1px solid var(--rand); margin-top: 1rem; padding-top: .8rem; }
.geschiedenis > div { margin-bottom: .2rem; }
/* Eén bijlage in het rijtje onder een kluisitem, persoon of bericht. */
.bijlage { border: 1px solid var(--rand); border-radius: 10px; padding: .5rem .6rem; }
.bijlage .teken { font-size: 1.2rem; line-height: 1; }
/* De naam en het tekentje samen zijn de knop om een bijlage te bekijken. Ziet eruit als tekst,
gedraagt zich als een knop - dat laatste is wat een schermlezer nodig heeft. */
.bijlage-openen {
display: flex; align-items: center; gap: .6rem;
border: none; background: none; padding: 0; margin: 0;
font: inherit; color: inherit; text-align: left; cursor: pointer;
min-width: 0;
}
.bijlage-openen:disabled { cursor: default; opacity: .6; }
.bijlage-openen > .groei { display: flex; flex-direction: column; min-width: 0; }
/* Een naam die niet past hoort af te breken, niet de knoppen ernaast weg te duwen. Een foto van
de camera heette eerder 824c9d46a1b749ab9f92986b - die past nergens in. */
.bijlage-openen .naam { overflow-wrap: anywhere; }
/* Het plaatje of filmpje in de kijkpopup. Hoogte begrensd, anders moet je op een telefoon
scrollen om de sluitknop terug te vinden. */
.bijlage-groot {
display: block; max-width: 100%; max-height: 60vh;
margin: 0 auto; border-radius: 10px;
}
/* Het voorbeeldje in de rij zelf, even groot als het tekentje ernaast zou zijn. */
.bijlage-klein {
width: 40px; height: 40px; flex: none; display: block;
border-radius: 8px; object-fit: cover;
}
.bijlage a { overflow-wrap: anywhere; }
/* ------------------------------------------------------------------ popup */
.popup-achtergrond {
position: fixed; inset: 0; z-index: 50;
background: rgba(20, 18, 16, .45);
display: flex; align-items: flex-end; justify-content: center;
padding: 1rem;
}
.popup {
width: 100%; max-width: 520px; max-height: 85dvh; overflow-y: auto;
background: var(--vlak); border-radius: var(--straal);
padding: 1.2rem 1.25rem 0;
box-shadow: var(--schaduw);
}
.popup h2 { margin-top: 0; }
/* Blijft onderin beeld staan terwijl de inhoud erboven scrolt - bij een lang formulier
(bijvoorbeeld een uitvaartwens met veel velden) zag je Opslaan en Annuleren anders pas na
helemaal naar beneden scrollen. sticky werkt hier omdat .popup zelf de scrollende ouder is
(overflow-y: auto hierboven); de negatieve margin trekt de balk weer tot de randen van de
popup, en de eigen achtergrond zorgt dat tekst er niet doorheen scrolt. */
.popup-knoppen {
position: sticky; bottom: 0; z-index: 1;
margin: 1rem -1.25rem 0; padding: .8rem 1.25rem calc(.8rem + env(safe-area-inset-bottom));
background: var(--vlak);
box-shadow: 0 -6px 10px -8px rgba(20, 18, 16, .35);
}
.popup-knoppen .knop { flex: 1 1 auto; }
/* De vaste Opslaan-knop op een volledig scherm (zoals Mijn profiel), niet in een popup. Hij
hoort net als bij een popup altijd in beeld te blijven, ook als het formulier eronder lang
is - anders scrol je een heel scherm omhoog om op te slaan. Op de telefoon zweeft hij boven
de onderbalk; op een groot scherm is er geen onderbalk meer, dus dan zakt hij naar onderen. */
.knop-vast-onder {
position: sticky; bottom: calc(4.7rem + env(safe-area-inset-bottom)); z-index: 5;
margin: 1rem -1.25rem 0; padding: .8rem 1.25rem;
background: var(--vlak);
box-shadow: 0 -6px 10px -8px rgba(20, 18, 16, .35);
}
@media (min-width: 720px) {
.knop-vast-onder { bottom: 0; }
}
/* Op een groter scherm hoort hij midden in beeld, niet onderaan. */
@media (min-width: 620px) {
.popup-achtergrond { align-items: center; }
}
/* ------------------------------------------------------------------ meldingen */
.melding {
border-radius: 10px; padding: .8rem 1rem; margin-bottom: 1rem;
border: 1px solid var(--rand); background: var(--vlak-zacht); font-size: .95rem;
}
.melding.fout { border-color: var(--fout); color: var(--fout); background: transparent; }
.melding.let-op { border-color: var(--waarschuwing); background: var(--waarschuwing-zacht); color: var(--waarschuwing); }
.melding.goed { border-color: var(--accent); background: var(--accent-zacht); color: var(--accent-donker); }
.label {
display: inline-block; font-size: .76rem; font-weight: 600;
padding: .18rem .6rem; border-radius: 999px;
background: var(--vlak-zacht); color: var(--inkt-zacht); border: 1px solid var(--rand);
}
.label.geclaimd { background: var(--accent-zacht); color: var(--accent-donker); border-color: var(--accent); }
.label.uitgenodigd { background: var(--waarschuwing-zacht); color: var(--waarschuwing); border-color: var(--waarschuwing); }
/* De ronde penning bij een persoon: zijn profielfoto, of anders zijn beginletters. Beide
precies even groot, zodat een lijst niet verspringt zodra er ergens een foto bij komt. */
.penning {
width: 44px; height: 44px; flex: none; border-radius: 50%;
background: var(--accent-zacht); color: var(--accent-donker);
display: flex; align-items: center; justify-content: center;
font-weight: 600; font-size: .95rem;
overflow: hidden;
}
/* object-fit: cover snijdt bij in plaats van uit te rekken - een portret dat staand is
gemaakt hoort niet breed getrokken te worden.
Let op de twee gevallen apart. Een img DIE de penning is houdt de 44 bij 44 hierboven; hem
hier op 100% zetten ging mis, want "img.penning" weegt zwaarder dan ".penning" en dan werd
de foto zo groot als de kaart. Een img BINNEN een penning vult hem juist wel. */
img.penning { object-fit: cover; display: block; }
.penning img { width: 100%; height: 100%; object-fit: cover; display: block; }
.leeg { text-align: center; padding: 2.5rem 1rem; color: var(--inkt-zacht); }
.leeg .teken { font-size: 2.2rem; display: block; margin-bottom: .5rem; opacity: .55; }
.voortgang { height: 8px; border-radius: 999px; background: var(--vlak-zacht); overflow: hidden; }
.voortgang > span { display: block; height: 100%; background: var(--merk); }
/* --------------------------------------------------------------- statistieken (beheer) */
.statgrid {
display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
gap: .8rem; margin: .8rem 0 1.2rem;
}
.statcel {
background: var(--vlak-zacht); border-radius: var(--straal); padding: .8rem 1rem;
}
.statgetal { display: block; font-size: 1.6rem; font-weight: 600; color: var(--accent-donker); }
.statlabel { display: block; font-size: .85rem; color: var(--inkt-zacht); margin-top: .1rem; }
.grafiek { margin: .5rem 0 .5rem; }
.grafieksvg { width: 100%; height: 160px; display: block; }
.grafiek-as { stroke: var(--rand); stroke-width: 1; }
.grafiek-lijn { fill: none; stroke-width: 2.5; }
.grafiek-lijn-profielen { stroke: var(--accent); }
.grafiek-lijn-kluis { stroke: var(--waarschuwing); }
.grafiek-onderschrift { display: flex; justify-content: space-between; margin-top: .2rem; }
.grafiek-legenda { display: flex; align-items: center; flex-wrap: wrap; gap: .3rem; margin-top: .6rem; }
.grafiek-legendapunt {
display: inline-block; width: .7rem; height: .7rem; border-radius: 999px;
background: var(--accent); margin-right: .4rem;
}
.grafiek-legendapunt.grafiek-lijn-kluis { background: var(--waarschuwing); }
hr { border: none; border-top: 1px solid var(--rand); margin: 1.25rem 0; }
.middenblok { max-width: 420px; margin: 3rem auto; }
/* Op een telefoon staat het logo al niet meer boven het inlogscherm (zie hierboven); die
3rem erboven was juist bedoeld om ruimte te maken naast een balk die verder leeg was. Zonder
logo mag "Log in" gewoon vlak onder de balk beginnen. */
@media (max-width: 480px) {
.inlog-scherm { margin-top: 1rem; }
}
#blazor-error-ui {
position: fixed; bottom: 0; left: 0; right: 0; z-index: 1000;
background: var(--waarschuwing-zacht); color: var(--waarschuwing);
padding: .8rem 1.2rem; display: none; box-shadow: var(--schaduw);
}
#blazor-error-ui .dismiss { cursor: pointer; float: right; }
/* --------------------------------------------------- inloggen met een code */
.codeveld {
font-size: 1.7rem; letter-spacing: .5rem; text-align: center;
font-family: "Segoe UI", system-ui, monospace; font-variant-numeric: tabular-nums;
width: 100%;
}
/* Op een smalle telefoon past zes cijfers met een halve rem ertussen net niet. */
@media (max-width: 420px) {
.codeveld { font-size: 1.45rem; letter-spacing: .3rem; padding-left: .4rem; padding-right: .4rem; }
}
/* De uitleg achter het vraagteken; werkt zonder javascript. */
.uitklap { margin: .4rem 0 1rem; }
.uitklap summary {
cursor: pointer; color: var(--accent-donker); font-size: .9rem;
list-style: none; display: inline-flex; align-items: center; gap: .4rem;
}
.uitklap summary::-webkit-details-marker { display: none; }
.uitklap summary::before {
content: "?"; width: 20px; height: 20px; border-radius: 50%;
background: var(--accent-zacht); color: var(--accent-donker);
display: inline-flex; align-items: center; justify-content: center;
font-weight: 700; font-size: .78rem; flex: none;
}
.uitklap p { margin: .6rem 0 0; font-size: .9rem; color: var(--inkt-zacht); }
.naar-adres { font-weight: 600; }
.voet { text-align: center; margin-top: 2rem; font-size: .9rem; }
.voet a { color: var(--inkt-zacht); }
Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

+33
View File
@@ -0,0 +1,33 @@
// Springt naar een veld dat niet goed is ingevuld: scrollt het in beeld, geeft het de focus en
// zet er een rode rand omheen tot iemand er iets aan verandert. Eén plek voor dit gedrag in
// plaats van dat elk scherm het zelf opnieuw uitvindt - zie .veld-fout in app.css.
window.mijnbesluit = window.mijnbesluit || {};
window.mijnbesluit.springNaarVeld = function (id) {
const veld = document.getElementById(id);
if (!veld) return;
veld.scrollIntoView({ behavior: "smooth", block: "center" });
veld.classList.add("veld-fout");
veld.focus({ preventScroll: true });
const weghalen = () => veld.classList.remove("veld-fout");
veld.addEventListener("input", weghalen, { once: true });
veld.addEventListener("change", weghalen, { once: true });
};
// Laat een tekstvak meegroeien met de inhoud, in plaats van een schuifbalk te tonen. Gebruikt
// voor Muziek en Verdere wensen bij een kluisitem: net zo prettig als je in één keer wilt zien
// wat je hebt opgeschreven. Zie textarea[data-meegroeit] in app.css.
window.mijnbesluit.laatMeegroeien = function (id) {
const veld = document.getElementById(id);
if (!veld || veld.dataset.meegroeit) return;
veld.dataset.meegroeit = "1";
const passenOpInhoud = () => {
veld.style.height = "auto";
veld.style.height = veld.scrollHeight + "px";
};
veld.addEventListener("input", passenOpInhoud);
passenOpInhoud();
};