diff options
Diffstat (limited to 'src')
7 files changed, 723 insertions, 742 deletions
diff --git a/src/main/java/eu/mulk/aendggner/synopse/HtmlRenderer.java b/src/main/java/eu/mulk/aendggner/synopse/HtmlRenderer.java index 09a60de..6357b29 100644 --- a/src/main/java/eu/mulk/aendggner/synopse/HtmlRenderer.java +++ b/src/main/java/eu/mulk/aendggner/synopse/HtmlRenderer.java @@ -33,7 +33,7 @@ public final class HtmlRenderer { .append(esc(synopse.alt().jurabk())) .append("</title>\n<style>\n") .append(CSS) - .append("</style>\n</head>\n<body>\n"); + .append("</style>\n</head>\n<body>\n<div class=\"bogen\">\n"); rendereKopf(sb, synopse, quelleBeschreibung, entwurfsfassung); @@ -55,71 +55,76 @@ public final class HtmlRenderer { rendereFuss(sb); - sb.append("</body>\n</html>\n"); + sb.append("</div>\n</body>\n</html>\n"); return sb.toString(); } /** - * Der Briefkopf des Vordrucks ÄG-2, im Aufbau derselbe wie auf der Aufgabeseite: links die - * ausstellende Stelle, rechts die Kennung; darunter der Vorspann mit Herkunft und Statistik. + * Der Kopf des Vordrucks ÄG-2: Titel, ausstellende Stelle, darunter der Vorspann als + * Beschriftungsstreifen — grau, was die Stelle gedruckt hat, weiß die Eintragung daneben. */ private static void rendereKopf( StringBuilder sb, Synopse synopse, String quelle, boolean entwurfsfassung) { - sb.append("<header class=\"briefkopf\">\n<div class=\"dienststelle\">\n"); - sb.append("<p class=\"langname\">").append(LANGNAME).append("</p>\n"); - sb.append("<h1>Synopse: ").append(esc(synopse.alt().jurabk())).append("</h1>\n"); - if (synopse.alt().langue() != null) { - sb.append("<p class=\"langue\">").append(esc(synopse.alt().langue())).append("</p>\n"); - } - sb.append("</div>\n<dl class=\"kennung\">\n"); - sb.append("<dt>Formblatt</dt><dd>ÄG‑2</dd>\n"); - sb.append("<dt>Stammgesetz</dt><dd>") + sb.append("<header>\n<h1>Synopse zum Stammgesetz ") .append(esc(synopse.alt().jurabk())) - .append("</dd>\n<dt>Erstellt</dt><dd>") - .append(LocalDate.now()) - .append("</dd>\n"); - if (entwurfsfassung) { - sb.append("<dt>Stand</dt><dd>Entwurfsfassung</dd>\n"); - } - sb.append("<dt>Geltung</dt><dd>nichtamtlich</dd>\n"); - sb.append("</dl>\n</header>\n"); + .append("</h1>\n<p class=\"stelle\">") + .append(LANGNAME) + .append("</p>\n</header>\n"); - sb.append("<div class=\"vorgang\">\n"); if (entwurfsfassung) { sb.append( - "<p class=\"entwurfshinweis\">Entwurfsfassung — nicht geltendes Recht. Die neue Fassung" - + " gibt den Stand des Gesetzgebungsverfahrens wieder.</p>\n"); + "<p class=\"entwurfshinweis\"><strong>Entwurfsfassung — nicht geltendes Recht.</strong>" + + " Die neue Fassung gibt den Stand des Gesetzgebungsverfahrens wieder.</p>\n"); } - sb.append("<p class=\"quelle\">Grundlage: ").append(esc(quelle)).append("</p>\n"); + long geaendert = synopse.eintraege().stream() .filter(e -> e.art() != Synopse.Aenderungsart.UNVERAENDERT) .count(); - sb.append("<p class=\"statistik\">") - .append(geaendert) - .append(" geänderte Normen, ") - .append(synopse.manuellZuPruefen().size()) - .append(" manuell zu prüfende Befehle</p>\n"); - sb.append("</div>\n"); + sb.append("<dl class=\"vorspann\">\n"); + vorspannZeile(sb, "Stammgesetz", synopse.alt().jurabk()); + if (synopse.alt().langue() != null) { + vorspannZeile(sb, "Langbezeichnung", synopse.alt().langue()); + } + vorspannZeile(sb, "Grundlage", quelle); + vorspannZeile(sb, "Erstellt am", LocalDate.now().toString()); + vorspannZeile( + sb, + "Umfang", + geaendert + + " geänderte Normen, " + + synopse.manuellZuPruefen().size() + + " manuell zu prüfende Befehle"); + vorspannZeile(sb, "Geltung", entwurfsfassung ? "nichtamtlich, Entwurfsstand" : "nichtamtlich"); + sb.append("</dl>\n"); + } + + private static void vorspannZeile(StringBuilder sb, String bezeichnung, String wert) { + sb.append("<dt>") + .append(esc(bezeichnung)) + .append("</dt><dd>") + .append(esc(wert)) + .append("</dd>\n"); } /** - * Der Fußsteg. Der Vorbehalt gehört gerade hierher und nicht bloß auf die Aufgabeseite: Die - * Synopse wird ausgedruckt und weitergereicht, das Formular nicht. + * Der Fußsteg trägt wie im Muster die Vordrucknummer links und den Ausgabestand rechts. Der + * Vorbehalt gehört gerade hierher und nicht bloß auf die Aufgabeseite: Die Synopse wird + * ausgedruckt und weitergereicht, das Formular nicht. */ private static void rendereFuss(StringBuilder sb) { - sb.append("<footer>\n<span>Erstellt am ") - .append(LocalDate.now()) - .append(" mit ÄndGgner</span>\n") + sb.append("<footer>\n<span class=\"vordrucknummer\">ÄG‑2</span>\n") .append( - "<span class=\"nebenquelle\">Ohne Gewähr; maßgeblich ist allein die amtliche" + "<span>Erstellt mit ÄndGgner. Ohne Gewähr; maßgeblich ist allein die amtliche" + " Verkündung im jeweiligen Gesetz- oder Verordnungsblatt.</span>\n") - .append("</footer>\n"); + .append("<span class=\"ausgabe\">") + .append(LocalDate.now()) + .append("</span>\n</footer>\n"); } private static final String LANGNAME = - "Nichtamtliche Zentralstelle für die maschinelle Fortschreibung von Stammgesetzen anhand" - + " von Änderungsvorschriften des Bundes und der Länder"; + "ÄndGgner — nichtamtliche Zentralstelle für die maschinelle Fortschreibung von Stammgesetzen" + + " anhand von Änderungsvorschriften des Bundes und der Länder"; private static void rendereGliederungsAenderungen(StringBuilder sb, Synopse synopse) { if (synopse.gliederungsAenderungen().isEmpty()) { @@ -251,45 +256,50 @@ public final class HtmlRenderer { private static final String CSS = """ - /* Der Vordruck ÄG-2. Das Farbgerüst ist dasselbe wie in - src/main/resources/eu/mulk/aendggner/web/style.css und wird hier ein zweites Mal - geführt: Diese Datei wird aus einem blob:-Verweis geöffnet und im Befehlszeilen- - betrieb als einzelne Datei abgelegt; sie kann kein fremdes Stilblatt einbinden. - Wer dort etwas ändert, ändere es hier mit. - - Die Schrift ist geteilt: Der Vordruck — Briefkopf, Kennung, Überschriften, - Spaltenköpfe, Kästchen, Fußsteg — steht in der Groteske, der Wortlaut der Normen - in der Antiqua. Amtskopf und Vorschrift sind zweierlei, im Gesetzblatt wie hier. - - Farbe trägt allein die Änderung: rot heißt weg, grün heißt hinzu. Damit die - Aussage auch im Schwarzweiß-Ausdruck und bei Farbfehlsichtigkeit erhalten bleibt, - tritt zur Farbe stets die Form — durchgestrichen gegen unterstrichen — sowie die - Spalte, in der sie steht. */ + /* Der Vordruck ÄG-2, gesetzt nach demselben Muster wie die Aufgabeseite (siehe + src/main/resources/eu/mulk/aendggner/web/style.css): weißes Papier, schwarze + Haarlinien, grauer Raster für alles, was die Stelle gedruckt hat. Das Farbgerüst + wird hier ein zweites Mal geführt, weil diese Datei aus einem blob:-Verweis + geöffnet und im Befehlszeilenbetrieb als einzelne Datei abgelegt wird; sie kann + kein fremdes Stilblatt einbinden. Wer dort etwas ändert, ändere es hier mit. + + Die Schrift ist geteilt: Der Vordruck steht in der Groteske, der Wortlaut der + Normen in der Antiqua. Amtskopf und Vorschrift sind zweierlei, im Gesetzblatt wie + hier. + + Schmuckfarbe gibt es nicht. Farbe trägt allein die Änderung: rot heißt weg, grün + heißt hinzu. Damit die Aussage auch im Schwarzweiß-Ausdruck und bei + Farbfehlsichtigkeit erhalten bleibt, tritt zur Farbe stets die Form — + durchgestrichen gegen unterstrichen — sowie die Spalte, in der sie steht. */ :root { color-scheme: light dark; - --fg: #111; - --bg: #fff; - --papier: #f3f1ea; - --muted: #595959; - --raster: #d6d2c6; - --kasten: #f0ede4; - --amt: #8b0f21; + --fg: #000; + --papier: #fff; + --feld: #fff; + --grund: #d5d5d5; + --tint: #e4e4e4; + --linie: #000; + --muted: #444; + --tisch: #9a9a9a; --del-bg: #ffd7d7; --del-fg: #8b0000; --ins-bg: #d7f5d7; --ins-fg: #005f00; - --grotesk: system-ui, "Segoe UI", "Helvetica Neue", Arial, sans-serif; + --grotesk: Arial, "Helvetica Neue", Helvetica, system-ui, sans-serif; --antiqua: Georgia, "Times New Roman", serif; } + /* Im Dunkeln kehrt sich nicht die Farbe um, sondern das Verhältnis: Der Grund der + Stelle bleibt heller als das Feld. */ @media (prefers-color-scheme: dark) { :root { - --fg: #e6e6e6; - --bg: #1c1c1c; - --papier: #0f0f0f; - --muted: #a0a0a0; - --raster: #3c3c3c; - --kasten: #242424; - --amt: #e79aa4; + --fg: #e8e8e8; + --papier: #1a1a1a; + --feld: #101010; + --grund: #333333; + --tint: #242424; + --linie: #7a7a7a; + --muted: #a8a8a8; + --tisch: #000; --del-bg: #5a1f1f; --del-fg: #ffb3b3; --ins-bg: #1f4a1f; @@ -297,153 +307,136 @@ public final class HtmlRenderer { } } * { box-sizing: border-box; } - /* Die Unterlage endet nie, auch wenn der Bogen kürzer ist als das Fenster. */ - html { background: var(--papier); } + html { background: var(--tisch); } body { counter-reset: norm; + margin: 0; + padding: 1.5rem 1rem; font-family: var(--grotesk); - line-height: 1.5; - max-width: 90rem; - margin: 2rem auto 3rem; - padding: 2rem 2.25rem 2.5rem; - border: 1px solid var(--fg); + font-size: 0.8125rem; + line-height: 1.4; color: var(--fg); - background: var(--bg); } - /* Auf schmalen Geräten wäre der Bogenrand nur ein Verlust an Satzbreite. */ - @media (max-width: 48rem) { - body { margin: 0; padding: 1.25rem 1rem 2rem; border: none; } + /* Ein Blatt Papier hat keinen Rahmen; die Kante entsteht aus dem Farbunterschied zur + Unterlage. Die Breite ist größer als beim Antrag, weil zwei Spalten sie brauchen. */ + .bogen { + max-width: 88rem; + margin: 0 auto; + padding: 1.75rem 1.75rem 0.75rem; + background: var(--papier); } - - /* Briefkopf und Doppellinie wie auf der Aufgabeseite: kräftig in Amtsrot, dünn in - Rasterfarbe, wobei die zweite Linie der Oberrand des Vorspanns ist. */ - .briefkopf { - display: grid; - grid-template-columns: 1fr auto; - gap: 1rem 2rem; - align-items: end; - padding-bottom: 0.7rem; - border-bottom: 3px solid var(--amt); + @media (max-width: 40rem) { + body { padding: 0; } + .bogen { padding: 1rem 0.75rem 0.5rem; } } - .langname { - margin: 0 0 0.3rem; - max-width: 30rem; + + h1 { margin: 0 0 0.5rem; font-size: 1rem; line-height: 1.35; } + .stelle { margin: 0 0 0.9rem; font-size: 0.6875rem; color: var(--muted); } + + .entwurfshinweis { + margin: 0 0 0.9rem; + padding: 0.45rem 0.6rem; + border: 1px solid var(--linie); + border-left: 4px solid var(--del-fg); + background: var(--feld); font-size: 0.75rem; - letter-spacing: 0.05em; - text-transform: uppercase; - line-height: 1.35; - color: var(--muted); } - .dienststelle h1 { - margin: 0; - font-size: 1.9rem; - letter-spacing: 0.06em; - line-height: 1.1; - } - .langue { margin: 0.35rem 0 0; font-style: italic; color: var(--muted); } - .kennung { + + /* Der Vorspann ist der Beschriftungsstreifen des Musters: links grau die Bezeichnung, + rechts weiß die Eintragung, Zelle an Zelle ohne Abstand. */ + .vorspann { display: grid; - grid-template-columns: auto auto; - gap: 0 0.6rem; + grid-template-columns: 10rem 1fr; margin: 0; - padding: 0.4rem 0.6rem; - border: 1px solid var(--raster); - font-size: 0.75rem; - line-height: 1.5; - } - .kennung dt { letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted); } - .kennung dd { margin: 0; font-weight: bold; color: var(--amt); white-space: nowrap; } - /* Gestapelt zöge die Kennung sonst über die ganze Breite und sähe aus wie ein Kasten - des Satzspiegels statt wie ein Stempel am Kopfrand. */ - @media (max-width: 34rem) { - .briefkopf { grid-template-columns: 1fr; align-items: start; } - .kennung { justify-self: start; } + border-top: 1px solid var(--linie); + border-left: 1px solid var(--linie); + font-size: 0.6875rem; } - - .vorgang { margin-top: 3px; padding-top: 1rem; border-top: 1px solid var(--raster); } - .quelle, .statistik, .gliederung, .ursachen { - color: var(--muted); - font-size: 0.85rem; - margin: 0.2rem 0; + .vorspann dt { + padding: 0.2rem 0.5rem; + border-right: 1px solid var(--linie); + border-bottom: 1px solid var(--linie); + background: var(--grund); + font-weight: bold; } - .entwurfshinweis { - margin: 0 0 0.9rem; - padding: 0.9rem 1.1rem; - border: 1px solid var(--raster); - border-left: 4px solid var(--del-fg); - background: var(--kasten); - font-size: 0.9rem; + .vorspann dd { + margin: 0; + padding: 0.2rem 0.5rem; + border-right: 1px solid var(--linie); + border-bottom: 1px solid var(--linie); + background: var(--feld); + overflow-wrap: anywhere; } - /* Der Spaltenkopf steht mit der Gegenüberstellung in einem Block und nicht mehr im - Kopfblock: Nur so reicht sein Klebebereich über alle Normen und nicht bloß über den - Vorspann. Seine beiden Felder tragen die Form der Legende des Antragsvordrucks. */ + /* Der Spaltenkopf steht mit der Gegenüberstellung in einem Block: Sein Klebebereich + reicht damit über alle Normen und endet vor dem Abschnitt „Manuell prüfen“. Seine + beiden Felder sind Beschriftungsstreifen wie im Vordruck, also grau. */ .spaltenkopf { display: grid; grid-template-columns: 1fr 1fr; - gap: 1rem; + gap: 0.75rem; position: sticky; top: 0; z-index: 1; margin-top: 1.25rem; - padding: 0.6rem 0 0.6rem 3rem; - background: var(--bg); + padding: 0.5rem 0 0.5rem 2.5rem; + background: var(--papier); } .spaltenkopf div { - padding: 0.3rem 0.8rem; - background: var(--fg); - color: var(--bg); - font-size: 0.8rem; + padding: 0.25rem 0.5rem; + border: 1px solid var(--linie); + background: var(--grund); + font-size: 0.6875rem; font-weight: bold; - letter-spacing: 0.08em; - text-transform: uppercase; } - /* Der Steg links trägt die Randziffer, wie im Vordruck die Feldziffer; sie macht die - Abschnitte überdies zitierbar. */ - section { position: relative; padding-left: 3rem; } + /* Der Steg links trägt die Randziffer in einem Kästchen, wie der Antrag das + Ankreuzfeld; sie macht die Abschnitte überdies zitierbar. */ + section { position: relative; padding-left: 2.5rem; } section.norm { counter-increment: norm; - padding-top: 1rem; - padding-bottom: 1rem; - border-bottom: 1px solid var(--raster); + padding-top: 0.9rem; + padding-bottom: 0.9rem; + border-bottom: 1px solid var(--linie); } section.norm::before { content: counter(norm); position: absolute; - top: 1.15rem; + top: 1rem; left: 0; width: 1.5rem; - height: 1.5rem; - border: 1px solid var(--amt); - color: var(--amt); - font-size: 0.8rem; + height: 1.2rem; + border: 1px solid var(--linie); + background: var(--grund); + font-size: 0.6875rem; font-weight: bold; - line-height: 1.4rem; + line-height: 1.1rem; text-align: center; } - section.norm h2 { font-size: 1.05rem; margin: 0 0 0.5rem; letter-spacing: 0.02em; } + section.norm h2 { font-size: 0.8125rem; margin: 0 0 0.15rem; } + .gliederung, .ursachen { color: var(--muted); font-size: 0.6875rem; margin: 0.1rem 0; } .vergleich { display: grid; grid-template-columns: 1fr 1fr; - gap: 1rem; + gap: 0.75rem; align-items: start; + margin-top: 0.5rem; } - /* Die alte Fassung liegt auf dem Kastenton wie ein Durchschlag, die neue auf - Bogenweiß; beim Springen zwischen den Spalten ist damit ohne Hinsehen klar, wo man + /* Die alte Fassung liegt auf dem hellen Raster wie ein Durchschlag, die neue auf dem + weißen Feld; beim Springen zwischen den Spalten ist damit ohne Hinsehen klar, wo man steht. Die Hinterlegungen der Änderungen heben sich von beiden Gründen ab. */ .vergleich > div { - padding: 0.75rem 0.9rem; - border: 1px solid var(--raster); + padding: 0.5rem 0.6rem; + border: 1px solid var(--linie); white-space: pre-wrap; overflow-wrap: anywhere; font-family: var(--antiqua); - font-size: 0.95rem; + font-size: 0.875rem; line-height: 1.45; } - .vergleich > .alt { background: var(--kasten); } - .vergleich > .neu { background: var(--bg); } + .vergleich > .alt { background: var(--tint); } + .vergleich > .neu { background: var(--feld); } del, .alt del { background: var(--del-bg); @@ -455,111 +448,104 @@ public final class HtmlRenderer { color: var(--ins-fg); text-decoration: underline; } - .leer { color: var(--muted); font-style: italic; } + .leer { font-family: var(--grotesk); font-size: 0.75rem; color: var(--muted); } - /* Kästchen in Versalien; die Farbe folgt derselben Regel wie im Diff. Beschriftet - sind sie ohnehin, sodass die Farbe hier nur bestätigt, was dasteht. Der Rahmen - steht auch dort, wo die Hinterlegung schwach wirkt. */ + /* Kästchen wie die Ankreuzfelder des Antrags: eckig, haarfein umrandet. Beschriftet + sind sie ohnehin, sodass die Farbe nur bestätigt, was dasteht. */ .badge { font-family: var(--grotesk); - font-size: 0.7rem; + font-size: 0.625rem; font-weight: normal; text-transform: uppercase; - letter-spacing: 0.06em; - border: 1px solid var(--raster); - padding: 0.1rem 0.55rem; + border: 1px solid var(--linie); + padding: 0 0.4rem; vertical-align: middle; white-space: nowrap; } - .neu-badge { background: var(--ins-bg); color: var(--ins-fg); border-color: var(--ins-fg); } - .aufgehoben-badge { - background: var(--del-bg); - color: var(--del-fg); - border-color: var(--del-fg); - } - .geaendert-badge { border-color: var(--raster); color: var(--muted); } + .neu-badge { background: var(--ins-bg); color: var(--ins-fg); } + .aufgehoben-badge { background: var(--del-bg); color: var(--del-fg); } + .geaendert-badge { background: var(--grund); color: var(--fg); } - /* Die Abschnittsüberschriften treten als Balken auf wie die Legende des Feldblocks. */ + /* Die Abschnittsüberschriften sind Beschriftungsstreifen über die volle Breite, wie im + Muster die Zeile „Verfügung des Finanzamts“. */ section.gliederung-aenderungen > h2, section.manuell > h2 { - display: inline-block; - margin: 0 0 0.9rem; - padding: 0.3rem 0.8rem; - background: var(--fg); - color: var(--bg); - font-size: 0.8rem; - font-weight: bold; - letter-spacing: 0.08em; - text-transform: uppercase; + margin: 0 0 0.6rem; + padding: 0.25rem 0.5rem; + border: 1px solid var(--linie); + background: var(--grund); + font-size: 0.75rem; + text-align: center; } section.gliederung-aenderungen { padding-top: 1.25rem; } - section.manuell { margin-top: 2rem; padding-top: 1.5rem; border-top: 3px solid var(--amt); } - section.manuell h3 { - margin: 1.25rem 0 0.5rem; - font-size: 0.8rem; - letter-spacing: 0.06em; - text-transform: uppercase; - } - section.manuell li { margin-bottom: 0.7rem; } - .originaltext { font-family: var(--antiqua); color: var(--muted); font-size: 0.85rem; } + section.manuell { margin-top: 1.5rem; padding-top: 1.25rem; } + section.manuell h3 { margin: 1rem 0 0.4rem; font-size: 0.6875rem; } + section.manuell li { margin-bottom: 0.6rem; font-size: 0.75rem; } + .originaltext { font-family: var(--antiqua); color: var(--muted); font-size: 0.8125rem; } + /* Vordrucknummer links, Ausgabestand rechts, beide winzig und ohne Zierrat. */ footer { display: flex; flex-wrap: wrap; + align-items: baseline; justify-content: space-between; - gap: 0.5rem 1.5rem; - margin-top: 2.5rem; - padding-top: 0.9rem; - border-top: 1px solid var(--fg); - font-size: 0.8125rem; + gap: 0.4rem 1rem; + margin-top: 1.5rem; + padding: 0.4rem 0 0.6rem; + font-size: 0.625rem; color: var(--muted); } + .vordrucknummer, .ausgabe { white-space: nowrap; } /* Zwei Spalten Gesetzestext nebeneinander sind auf dem Telefon nicht zu lesen. Gestapelt trägt der Spaltenkopf nichts mehr; deshalb beschriftet sich dort jede Spalte selbst. */ @media (max-width: 60rem) { .spaltenkopf { display: none; } .vergleich { grid-template-columns: 1fr; } - section { padding-left: 2.25rem; } + .vorspann { grid-template-columns: 7rem 1fr; } + section { padding-left: 2rem; } .vergleich > div::before { display: block; - margin-bottom: 0.4rem; + margin: -0.5rem -0.6rem 0.4rem; + padding: 0.15rem 0.6rem; + border-bottom: 1px solid var(--linie); + background: var(--grund); font-family: var(--grotesk); - font-size: 0.7rem; + font-size: 0.625rem; font-weight: bold; - letter-spacing: 0.08em; - text-transform: uppercase; - color: var(--muted); } .vergleich > .alt::before { content: "Alte Fassung"; } .vergleich > .neu::before { content: "Neue Fassung"; } } /* Gedruckt wird stets auf weißes Papier, auch aus einem dunkel eingestellten Browser - heraus: prefers-color-scheme gilt im Druck fort. Deshalb ist hier das ganze - Farbgerüst zurückzusetzen und nicht nur ein Teil davon — sonst käme die alte Spalte - als schwarzer Block aus dem Drucker. */ + heraus: prefers-color-scheme gilt im Druck fort. Deshalb ist das ganze Farbgerüst + zurückzusetzen und nicht nur ein Teil davon — sonst käme die alte Spalte als + schwarzer Block aus dem Drucker. */ @media print { :root { --fg: #000; - --bg: #fff; --papier: #fff; + --feld: #fff; + --grund: #d5d5d5; + --tint: #e4e4e4; + --linie: #000; --muted: #333; - --raster: #999; - --kasten: #f2f0ea; - --amt: #000; + --tisch: #fff; --del-bg: #ffd7d7; --del-fg: #8b0000; --ins-bg: #d7f5d7; --ins-fg: #005f00; } - body { margin: 0; padding: 0; max-width: none; border: none; font-size: 10pt; } + body { padding: 0; font-size: 9pt; } + .bogen { max-width: none; padding: 0; } .spaltenkopf { position: static; } .vergleich, section.norm { break-inside: avoid; } - /* Ohne diese Festlegung wirft der Druck die Hinterlegung weg. Durchstreichung - und Unterstreichung überstehen ihn ohnehin und tragen die Aussage auch dort, - wo schwarzweiß gedruckt wird; der Spaltenbalken und der Durchschlagston der - alten Fassung aber haben keine solche zweite Gestalt. */ + /* Ohne diese Festlegung wirft der Druck die Hinterlegung weg. Durchstreichung und + Unterstreichung überstehen ihn ohnehin und tragen die Aussage auch dort, wo + schwarzweiß gedruckt wird; der Raster der Stelle aber hat keine solche zweite + Gestalt — er sagt, was gedruckt und was eingetragen ist. */ del, ins, .badge, .entwurfshinweis, .spaltenkopf div, .vergleich > .alt, + .vorspann dt, section.norm::before, section.gliederung-aenderungen > h2, section.manuell > h2 { -webkit-print-color-adjust: exact; print-color-adjust: exact; diff --git a/src/main/resources/eu/mulk/aendggner/web/datenschutz.html b/src/main/resources/eu/mulk/aendggner/web/datenschutz.html index b217b89..e88a699 100644 --- a/src/main/resources/eu/mulk/aendggner/web/datenschutz.html +++ b/src/main/resources/eu/mulk/aendggner/web/datenschutz.html @@ -10,20 +10,13 @@ <link rel="stylesheet" href="style.css"> </head> <body> - <header class="briefkopf"> - <div class="dienststelle"> - <p class="langname"> - Nichtamtliche Zentralstelle für die maschinelle Fortschreibung von Stammgesetzen - anhand von Änderungsvorschriften des Bundes und der Länder - </p> - <p class="wortmarke"><a href="index.html">ÄndGgner</a></p> - </div> +<div class="bogen"> - <dl class="kennung"> - <dt>Formblatt</dt><dd>ÄG‑1/B</dd> - <dt>Anlage</dt><dd>Datenschutz</dd> - <dt>Geltung</dt><dd>nichtamtlich</dd> - </dl> + <header> + <p class="stelle"> + ÄndGgner — nichtamtliche Zentralstelle für die maschinelle Fortschreibung von Stammgesetzen + anhand von Änderungsvorschriften des Bundes und der Länder + </p> </header> <main> @@ -126,8 +119,13 @@ <p>Stand: 22. August 2026.</p> </main> <footer> - <nav><a href="index.html">Zurück zum Formular</a></nav> - <span class="nebenquelle">(<a href="https://gerrit.benkard.de/plugins/gitiles/aendggner">Git</a>)</span> + <span class="vordrucknummer">ÄG‑1/B</span> + <nav> + <a href="index.html">Zurück zum Vordruck</a> · + <a href="impressum.html">Impressum</a> + </nav> + <span class="ausgabe">08.26</span> </footer> + +</div> </body> -</html> diff --git a/src/main/resources/eu/mulk/aendggner/web/favicon.svg b/src/main/resources/eu/mulk/aendggner/web/favicon.svg index 2f54d8f..b619c30 100644 --- a/src/main/resources/eu/mulk/aendggner/web/favicon.svg +++ b/src/main/resources/eu/mulk/aendggner/web/favicon.svg @@ -1,13 +1,13 @@ <!-- SPDX-FileCopyrightText: 2020 Matthias Andreas Benkard <code@mail.matthias.benkard.de> SPDX-License-Identifier: AGPL-3.0-or-later --> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" role="img" aria-label="ÄndGgner"> - <!-- Farben wie in style.css: Papier, Amtsrot als Rahmen des Vordrucks, schwarzes - Zeichen. Ein Favicon erbt keine CSS-Variablen, deshalb stehen die Werte hier + <!-- Farben wie in style.css: Vordruckgrau als Grund, ein weißes Feld, schwarze Haarlinie, + schwarzes Zeichen. Ein Favicon erbt keine CSS-Variablen, deshalb stehen die Werte hier ausgeschrieben. --> - <rect width="32" height="32" fill="#fff"/> - <rect x="1" y="1" width="30" height="30" fill="none" stroke="#8b0f21" stroke-width="2"/> - <text x="16" y="17.5" fill="#111" - font-family="system-ui, 'Segoe UI', 'Helvetica Neue', Arial, sans-serif" - font-size="21" font-weight="bold" text-anchor="middle" + <rect width="32" height="32" fill="#d5d5d5"/> + <rect x="0.5" y="0.5" width="31" height="31" fill="none" stroke="#000"/> + <rect x="4.5" y="8.5" width="23" height="19" fill="#fff" stroke="#000"/> + <text x="16" y="19" fill="#000" font-family="Arial, Helvetica, sans-serif" + font-size="17" font-weight="bold" text-anchor="middle" dominant-baseline="central">§</text> </svg> diff --git a/src/main/resources/eu/mulk/aendggner/web/impressum.html b/src/main/resources/eu/mulk/aendggner/web/impressum.html index cce8797..03f4063 100644 --- a/src/main/resources/eu/mulk/aendggner/web/impressum.html +++ b/src/main/resources/eu/mulk/aendggner/web/impressum.html @@ -10,20 +10,13 @@ <link rel="stylesheet" href="style.css"> </head> <body> - <header class="briefkopf"> - <div class="dienststelle"> - <p class="langname"> - Nichtamtliche Zentralstelle für die maschinelle Fortschreibung von Stammgesetzen - anhand von Änderungsvorschriften des Bundes und der Länder - </p> - <p class="wortmarke"><a href="index.html">ÄndGgner</a></p> - </div> +<div class="bogen"> - <dl class="kennung"> - <dt>Formblatt</dt><dd>ÄG‑1/A</dd> - <dt>Anlage</dt><dd>Impressum</dd> - <dt>Geltung</dt><dd>nichtamtlich</dd> - </dl> + <header> + <p class="stelle"> + ÄndGgner — nichtamtliche Zentralstelle für die maschinelle Fortschreibung von Stammgesetzen + anhand von Änderungsvorschriften des Bundes und der Länder + </p> </header> <main> @@ -72,8 +65,13 @@ </p> </main> <footer> - <nav><a href="index.html">Zurück zum Formular</a></nav> - <span class="nebenquelle">(<a href="https://gerrit.benkard.de/plugins/gitiles/aendggner">Git</a>)</span> + <span class="vordrucknummer">ÄG‑1/A</span> + <nav> + <a href="index.html">Zurück zum Vordruck</a> · + <a href="datenschutz.html">Datenschutz</a> + </nav> + <span class="ausgabe">08.26</span> </footer> + +</div> </body> -</html> diff --git a/src/main/resources/eu/mulk/aendggner/web/index.html b/src/main/resources/eu/mulk/aendggner/web/index.html index 26ff522..4a0bbdd 100644 --- a/src/main/resources/eu/mulk/aendggner/web/index.html +++ b/src/main/resources/eu/mulk/aendggner/web/index.html @@ -15,152 +15,165 @@ <script src="app.js" defer></script> </head> <body> - <header class="briefkopf"> - <div class="dienststelle"> - <p class="langname"> - Nichtamtliche Zentralstelle für die maschinelle Fortschreibung von Stammgesetzen - anhand von Änderungsvorschriften des Bundes und der Länder - </p> - <h1>ÄndGgner</h1> - </div> - - <dl class="kennung"> - <dt>Formblatt</dt><dd>ÄG‑1</dd> - <dt>Vorgang</dt><dd>Synopse</dd> - <dt>Geltung</dt><dd>nichtamtlich</dd> - </dl> +<div class="bogen"> + + <header> + <h1>Antrag auf Erstellung einer Synopse<br> + zu einem Stammgesetz nach Maßgabe eines oder mehrerer Änderungsdokumente</h1> + <p class="stelle"> + ÄndGgner — nichtamtliche Zentralstelle für die maschinelle Fortschreibung von Stammgesetzen + anhand von Änderungsvorschriften des Bundes und der Länder + </p> </header> - <main> - <p class="betreff"> - Änderungsgesetze automatisch auf ein Stammgesetz anwenden und als Synopse darstellen. + <section class="beachtung"> + <h2>Zur Beachtung:</h2> + <p> + Dieser Vordruck wendet ein oder mehrere Änderungsdokumente — verkündete Änderungsgesetze, + Gesetzentwürfe, Änderungsanträge und Beschlussempfehlungen — selbsttätig auf ein Stammgesetz + an und stellt das Ergebnis als zweispaltige Synopse gegenüber. Die Dokumentart wird aus dem + Text erkannt; eine Angabe darüber ist nicht zu machen. + </p> + <p> + Die gewählten Dateien <strong>verlassen Ihren Rechner nicht</strong>. Die gesamte Auswertung + läuft als WebAssembly-Programm in Ihrem Browser; es wird nichts hochgeladen und nichts + gespeichert. + </p> + <p> + Die Synopse wird automatisch erstellt und ist <strong>ohne Gewähr</strong> für Vollständigkeit + und Richtigkeit; sie ersetzt keine Rechtsberatung. Maßgeblich ist allein die amtliche + Verkündung im jeweiligen Gesetz- oder Verordnungsblatt. Uneindeutige Änderungsbefehle werden + zur manuellen Prüfung markiert statt stillschweigend verworfen. </p> - <noscript> - <aside class="kasten warnung"> - <h2>Hinweis</h2> - <p> - Dieses Werkzeug rechnet im Browser statt auf dem Server: Es braucht JavaScript und - WebAssembly. Ohne sie bleibt das nachstehende Formular wirkungslos. Wer beides nicht - einschalten möchte, verwende die Befehlszeilenfassung — sie liegt dem - <a href="aendggner-quelltext.tar.gz">Quelltext</a> bei und leistet dasselbe. - </p> - </aside> - </noscript> - - <details class="einfuehrung"> - <summary>Noch keine Dateien? Zwei Beispiele zum Ausprobieren</summary> - - <p> - Das Werkzeug braucht zweierlei: das <strong>Stammgesetz</strong> in seiner geltenden - Fassung und das <strong>Änderungsdokument</strong>, das es ändert. Beides liegt bei den - amtlichen Stellen bereit. + <p class="fehlt"> + Dieser Vordruck rechnet im Browser statt auf dem Server und braucht dafür JavaScript und + WebAssembly. Ohne sie bleibt er wirkungslos. Wer beides nicht einschalten möchte, verwende + die Befehlszeilenfassung — sie liegt dem <a href="aendggner-quelltext.tar.gz">Quelltext</a> + bei und leistet dasselbe. </p> - - <ol> - <li>Die beiden Dateien eines Beispiels herunterladen.</li> - <li>Sie in die Felder darunter eintragen — das xml.zip so, wie es kommt, ohne Entpacken.</li> - <li>„Synopse erzeugen“ drücken.</li> - </ol> - - <dl class="beispiele"> - <dt>Verkündetes Änderungsgesetz — Gesetz gegen den unlauteren Wettbewerb</dt> - <dd> - Stammgesetz: - <a href="https://www.gesetze-im-internet.de/uwg_2004/xml.zip" - target="_blank" rel="noopener noreferrer">UWG (xml.zip, 22 KB)</a><br> - Änderung: - <a href="https://www.recht.bund.de/bgbl/1/2026/43/regelungstext.pdf?__blob=publicationFile&v=1" - target="_blank" rel="noopener noreferrer">Drittes Gesetz zur Änderung des UWG, - BGBl. 2026 I Nr. 43 (PDF, 0,3 MB)</a><br> - <span class="hint">19 Befehle an 6 Normen, nichts manuell zu prüfen.</span> - </dd> - - <dt>Gesetzentwurf — Allgemeines Gleichbehandlungsgesetz</dt> - <dd> - Stammgesetz: - <a href="https://www.gesetze-im-internet.de/agg/xml.zip" - target="_blank" rel="noopener noreferrer">AGG (xml.zip, 14 KB)</a><br> - Änderung: - <a href="https://dserver.bundestag.de/btd/21/061/2106178.pdf" - target="_blank" rel="noopener noreferrer">Zweites Gesetz zur Änderung des AGG, - BT-Drs. 21/6178 (PDF, 0,5 MB)</a><br> - <span class="hint"> - 23 Befehle an 14 Normen, einer zur manuellen Prüfung markiert. Zeigt zugleich, was - gälte, wenn der Entwurf so beschlossen würde — der Begründungsteil dahinter erzeugt - keine Befehle. - </span> - </dd> - </dl> - - <p class="hint"> - Die Verweise führen zu gesetze-im-internet.de, recht.bund.de und dserver.bundestag.de. Das - Werkzeug ruft dort nichts von sich aus ab: Es sieht nur die Dateien, die Sie ihm im Formular - übergeben. - </p> - </details> - - <form id="synopse-formular"> - <fieldset> - <legend>Antrag auf Erstellung einer Synopse</legend> - - <div class="field"> - <label for="stamm">Stammgesetz</label> - <input type="file" id="stamm" name="stamm" required aria-describedby="stamm-hinweis"> - <p class="hint" id="stamm-hinweis"> - gii-Norm-XML von gesetze-im-internet.de — auch als heruntergeladenes xml.zip, ohne - Entpacken —, oder für Landesrecht die konsolidierte Fassung aus dem Landesportal (PDF - oder Klartext). - </p> + </noscript> + </section> + + <p class="ausfuellhinweis"> + Weiße Felder bitte ausfüllen oder <span class="kreuz">X</span> ankreuzen + </p> + + <form id="synopse-formular"> + <div class="vordruck"> + + <div class="block"> + <div class="leitspalte">Angaben zum Stammgesetz</div> + <div class="felder"> + <div class="feld"> + <label for="stamm">Stammgesetz in der geltenden Fassung + <span class="erl">(gii-Norm-XML von gesetze-im-internet.de — auch als + heruntergeladenes xml.zip, ohne Entpacken —, oder für Landesrecht die + konsolidierte Fassung aus dem Landesportal als PDF oder Klartext)</span></label> + <input type="file" id="stamm" name="stamm" required> + </div> </div> + </div> - <div class="field"> - <label for="aenderung">Änderungsdokument(e)</label> - <input type="file" id="aenderung" name="aenderung" multiple required - aria-describedby="aenderung-hinweis"> - <p class="hint" id="aenderung-hinweis"> - Ein oder mehrere PDFs oder Klartextdateien: Änderungsgesetze aus BGBl und GVBl, - Gesetzentwürfe (Referenten-, Regierungs- und Fraktionsentwürfe, Drucksachen) sowie - Änderungsanträge — letztere zusammen mit dem Entwurf hochladen, den sie ändern. Die - Dokumentart wird aus dem Text erkannt. Bei Mehrfachauswahl wird in der gewählten - Reihenfolge nacheinander angewendet. - </p> + <div class="block"> + <div class="leitspalte">Angaben zu den Änderungen</div> + <div class="felder"> + <div class="feld"> + <label for="aenderung">Änderungsdokument(e) + <span class="erl">(ein oder mehrere PDFs oder Klartextdateien: Änderungsgesetze aus + BGBl und GVBl, Gesetzentwürfe, Drucksachen sowie Änderungsanträge — letztere + zusammen mit dem Entwurf, den sie ändern. Bei Mehrfachauswahl wird in der + gewählten Reihenfolge nacheinander angewendet)</span></label> + <input type="file" id="aenderung" name="aenderung" multiple required> + </div> + <div class="feld ankreuzfeld"> + <label> + <input type="checkbox" id="vollstaendig" name="vollstaendig"> + Unveränderte Vorschriften mit in die Synopse aufnehmen + </label> + </div> </div> + </div> - <div class="field checkbox"> - <label> - <input type="checkbox" id="vollstaendig" name="vollstaendig"> - Unveränderte Vorschriften mit in die Synopse aufnehmen - </label> + <div class="block"> + <div class="leitspalte">Antragstellung</div> + <div class="felder"> + <div class="feld absenden"> + <button type="submit">Synopse erstellen</button> + <span class="erl">Die Auswertung läuft in diesem Browser; je nach Umfang der + Dokumente dauert sie einige Sekunden.</span> + </div> </div> - </fieldset> - - <div class="absenden"> - <button type="submit">Synopse erzeugen</button> </div> - </form> - - <p id="meldung" class="meldung" hidden aria-live="polite" aria-atomic="true"></p> - - <aside class="kasten disclaimer"> - <h2>Haftungsausschluss</h2> - <p> - Die Synopse wird automatisch erstellt und ist <strong>ohne Gewähr</strong> für Vollständigkeit - und Richtigkeit; sie ersetzt keine Rechtsberatung. Uneindeutige Änderungsbefehle werden zur - manuellen Prüfung markiert statt stillschweigend verworfen. Die gewählten Dateien - <strong>verlassen Ihren Rechner nicht</strong>: Die Auswertung läuft vollständig im Browser, - es wird nichts hochgeladen und nichts gespeichert. - </p> - </aside> - </main> + + </div> + </form> + + <section class="verfuegung"> + <h2>Verfügung der Zentralstelle</h2> + <div class="verfuegungsfeld"> + <p id="meldung" class="meldung" hidden aria-live="polite" aria-atomic="true"></p> + <p class="platzhalter">Wird nach Absenden des Antrags ausgefüllt.</p> + </div> + </section> + + <details class="ausfuellhilfe"> + <summary>Ausfüllhilfe — zwei Beispiele zum Ausprobieren</summary> + + <p> + Der Vordruck braucht zweierlei: das <strong>Stammgesetz</strong> in seiner geltenden Fassung + und das <strong>Änderungsdokument</strong>, das es ändert. Beides liegt bei den amtlichen + Stellen bereit. Die beiden Dateien eines Beispiels herunterladen, in die Felder oben + eintragen — das xml.zip so, wie es kommt, ohne Entpacken — und „Synopse erstellen“ drücken. + </p> + + <dl class="beispiele"> + <dt>Verkündetes Änderungsgesetz — Gesetz gegen den unlauteren Wettbewerb</dt> + <dd> + Stammgesetz: + <a href="https://www.gesetze-im-internet.de/uwg_2004/xml.zip" + target="_blank" rel="noopener noreferrer">UWG (xml.zip, 22 KB)</a><br> + Änderung: + <a href="https://www.recht.bund.de/bgbl/1/2026/43/regelungstext.pdf?__blob=publicationFile&v=1" + target="_blank" rel="noopener noreferrer">Drittes Gesetz zur Änderung des UWG, + BGBl. 2026 I Nr. 43 (PDF, 0,3 MB)</a><br> + <span class="erl">19 Befehle an 6 Normen, nichts manuell zu prüfen.</span> + </dd> + + <dt>Gesetzentwurf — Allgemeines Gleichbehandlungsgesetz</dt> + <dd> + Stammgesetz: + <a href="https://www.gesetze-im-internet.de/agg/xml.zip" + target="_blank" rel="noopener noreferrer">AGG (xml.zip, 14 KB)</a><br> + Änderung: + <a href="https://dserver.bundestag.de/btd/21/061/2106178.pdf" + target="_blank" rel="noopener noreferrer">Zweites Gesetz zur Änderung des AGG, + BT-Drs. 21/6178 (PDF, 0,5 MB)</a><br> + <span class="erl"> + 23 Befehle an 14 Normen, einer zur manuellen Prüfung markiert. Zeigt zugleich, was gälte, + wenn der Entwurf so beschlossen würde — der Begründungsteil dahinter erzeugt keine + Befehle. + </span> + </dd> + </dl> + + <p class="erl"> + Die Verweise führen zu gesetze-im-internet.de, recht.bund.de und dserver.bundestag.de. Der + Vordruck ruft dort nichts von sich aus ab: Er sieht nur die Dateien, die Sie ihm übergeben. + </p> + </details> <footer> + <span class="vordrucknummer">ÄG‑1</span> <nav> <a href="impressum.html">Impressum</a> · <a href="datenschutz.html">Datenschutz</a> · - <a href="aendggner-quelltext.tar.gz">Quelltext dieser Fassung (AGPLv3 oder später)</a> + <a href="aendggner-quelltext.tar.gz">Quelltext dieser Fassung (AGPLv3 oder später)</a> · + <a href="https://gerrit.benkard.de/plugins/gitiles/aendggner">Git</a> </nav> - <span class="nebenquelle">(<a href="https://gerrit.benkard.de/plugins/gitiles/aendggner">Git</a>)</span> + <span class="ausgabe">08.26</span> </footer> + +</div> </body> </html> diff --git a/src/main/resources/eu/mulk/aendggner/web/style.css b/src/main/resources/eu/mulk/aendggner/web/style.css index 81c16d7..36c2198 100644 --- a/src/main/resources/eu/mulk/aendggner/web/style.css +++ b/src/main/resources/eu/mulk/aendggner/web/style.css @@ -1,35 +1,43 @@ /* SPDX-FileCopyrightText: 2020 Matthias Andreas Benkard <code@mail.matthias.benkard.de> SPDX-License-Identifier: AGPL-3.0-or-later */ -/* Ein Vordruck, kein Aufsatz: Der Bogen liegt auf einer Unterlage, trägt oben einen - Briefkopf mit Formblattkennung und darunter einen Feldblock mit numerierten Feldern. - Farbe tritt an drei Stellen auf und meint dort jeweils dasselbe: Behördenrot führt - das Auge zu den Kennungen und Feldziffern, rot warnt, grün bestätigt. Kein Kasten - verlässt sich auf seine Farbe allein; Wort und Zeichen tragen die Aussage mit. */ +/* Ein Vordruck nach dem Vorbild der amtlichen Steuerformulare. Dort gilt eine einzige, + durchgehaltene Regel, und sie steht sogar im Formular selbst: „Weiße Felder bitte + ausfüllen“. Grau ist, was die Stelle gedruckt hat — Beschriftungen, Leitspalten, + Abschnittsköpfe, die Verfügung am Ende. Weiß ist, was der Antragsteller beiträgt. + Diese Regel trägt hier die gesamte Gestaltung. + + Alles andere folgt daraus: schwarze Haarlinien, aneinanderstoßende Zellen ohne Abstand + und ohne Rundung, kleine enge Groteske, Beschriftung innen oben links im Feld, + Erläuterungen in noch kleinerer Schrift dahinter, die Vordrucknummer winzig in den + Fußecken. Schmuckfarbe gibt es nicht: Rot und Grün treten allein dort auf, wo sie etwas + bedeuten — Fehler und Bestätigung —, und nie als einziger Träger der Aussage. */ :root { color-scheme: light dark; - --fg: #111; - --bg: #fff; - --papier: #f3f1ea; - --muted: #595959; - --border: #bbb; - --raster: #d6d2c6; - --kasten: #f0ede4; - --amt: #8b0f21; + --fg: #000; + --papier: #fff; + --feld: #fff; + --grund: #d5d5d5; + --tint: #e4e4e4; + --linie: #000; + --muted: #444; + --tisch: #9a9a9a; --warnung: #8b0000; --bestaetigung: #005f00; } +/* Im Dunkeln kehrt sich nicht die Farbe um, sondern das Verhältnis: Der Grund der Stelle + bleibt heller als das Feld, das auszufüllen ist. */ @media (prefers-color-scheme: dark) { :root { - --fg: #e6e6e6; - --bg: #1c1c1c; - --papier: #0f0f0f; - --muted: #a0a0a0; - --border: #4a4a4a; - --raster: #3c3c3c; - --kasten: #242424; - --amt: #e79aa4; + --fg: #e8e8e8; + --papier: #1a1a1a; + --feld: #101010; + --grund: #333333; + --tint: #242424; + --linie: #7a7a7a; + --muted: #a8a8a8; + --tisch: #000; --warnung: #ffb3b3; --bestaetigung: #b3ffb3; } @@ -39,286 +47,247 @@ box-sizing: border-box; } -/* Die Unterlage endet nie, auch wenn der Bogen kürzer ist als das Fenster. */ html { - background: var(--papier); + background: var(--tisch); } body { - margin: 2rem auto 3rem; - padding: 2rem 2.25rem 2.5rem; - max-width: 46rem; - border: 1px solid var(--fg); - font-family: system-ui, "Segoe UI", "Helvetica Neue", Arial, sans-serif; - font-size: 1rem; + margin: 0; + padding: 1.5rem 1rem; + font-family: Arial, "Helvetica Neue", Helvetica, system-ui, sans-serif; + font-size: 0.8125rem; + line-height: 1.4; color: var(--fg); - background: var(--bg); - line-height: 1.55; -} - -/* Auf schmalen Geräten wäre der Bogenrand nur ein Verlust an Satzbreite. */ -@media (max-width: 48rem) { - body { - margin: 0; - padding: 1.25rem 1rem 2rem; - border: none; - } -} - -/* Der Briefkopf: links die ausstellende Stelle, rechts die Kennung des Vordrucks. - Darunter die Doppellinie — kräftig in Amtsrot, dünn in Rasterfarbe —, deren zweite - Linie der Oberrand des Satzspiegels ist. */ -.briefkopf { - display: grid; - grid-template-columns: 1fr auto; - gap: 1rem 2rem; - align-items: end; - padding-bottom: 0.7rem; - border-bottom: 3px solid var(--amt); -} - -/* Gestapelt zöge die Kennung sonst über die ganze Breite und sähe aus wie ein Kasten - des Satzspiegels statt wie ein Stempel am Kopfrand. */ -@media (max-width: 34rem) { - .briefkopf { - grid-template-columns: 1fr; - align-items: start; - } - - .kennung { - justify-self: start; - } } -h1, -.wortmarke { - margin: 0; - font-size: 1.9rem; - font-weight: bold; - letter-spacing: 0.06em; - line-height: 1.1; +/* Der Bogen. Kein Rahmen: Ein Blatt Papier hat keinen, die Kante entsteht aus dem + Farbunterschied zur Unterlage. */ +.bogen { + max-width: 52rem; + margin: 0 auto; + padding: 1.75rem 1.75rem 0.75rem; + background: var(--papier); } -.wortmarke a { - text-decoration: none; +@media (max-width: 40rem) { + body { padding: 0; } + .bogen { padding: 1rem 0.75rem 0.5rem; } } -.langname { - margin: 0 0 0.3rem; - max-width: 26rem; - font-size: 0.75rem; - letter-spacing: 0.05em; - text-transform: uppercase; +h1 { + margin: 0 0 0.5rem; + font-size: 1rem; line-height: 1.35; - color: var(--muted); } -/* Die Kennung steht als Verzeichnis im Text und nicht als Zierrat im Stilblatt: Sie - lässt sich damit auswählen und vorlesen. „Nichtamtlich“ ist dabei keine Pointe, - sondern die Klarstellung, die die amtliche Anmutung nötig macht. */ -.kennung { - display: grid; - grid-template-columns: auto auto; - gap: 0 0.6rem; - margin: 0; - padding: 0.4rem 0.6rem; - border: 1px solid var(--raster); - font-size: 0.75rem; - line-height: 1.5; +.stelle { + margin: 0 0 0.9rem; + font-size: 0.6875rem; + color: var(--muted); } -.kennung dt { - letter-spacing: 0.06em; - text-transform: uppercase; - color: var(--muted); +/* Ohne Schmuckfarbe trägt allein die Unterstreichung das Klick-Signal. */ +a { + color: inherit; + text-decoration: underline; } -.kennung dd { - margin: 0; - font-weight: bold; - color: var(--amt); - white-space: nowrap; +/* Der Vorgabe-Umriss ist auf dem grauen Raster kaum auszumachen. */ +:focus-visible { + outline: 2px solid var(--fg); + outline-offset: 1px; } -main { - margin-top: 3px; - padding-top: 1.5rem; - border-top: 1px solid var(--raster); +/* „Zur Beachtung“ steht im Muster als weißer Kasten mit fetter Anrede und dichtem + Fließtext; die Überschrift bleibt in der Zeile des ersten Absatzes. */ +.beachtung { + padding: 0.6rem 0.7rem; + border: 1px solid var(--linie); + background: var(--feld); + font-size: 0.75rem; } -.betreff { - margin: 0 0 1.75rem; - font-size: 1.05rem; +.beachtung h2 { + margin: 0 0 0.35rem; + font-size: 0.75rem; } -/* Impressum und Datenschutz. */ -main > h1 { - margin-bottom: 1.25rem; - font-size: 1.4rem; +.beachtung p { + margin: 0 0 0.5rem; } -h2 { - margin: 1.75rem 0 0.5rem; - font-size: 0.9rem; - letter-spacing: 0.06em; - text-transform: uppercase; +.beachtung p:last-child { + margin-bottom: 0; } -address { - font-style: normal; - margin: 0.5rem 0; +.fehlt { + padding-left: 0.6rem; + border-left: 3px solid var(--warnung); + font-weight: bold; } -code { - font-size: 0.9375em; +/* Der Ausfüllhinweis ist im Muster ein eigener Streifen über dem Feldblock — er erklärt + genau die Regel, nach der dieses Blatt gesetzt ist. */ +.ausfuellhinweis { + margin: 0.9rem 0 0; + padding: 0.3rem 0.5rem; + border: 1px solid var(--linie); + border-bottom: none; + background: var(--grund); + font-size: 0.75rem; + font-weight: bold; } -/* Ohne Schmuckfarbe trägt allein die Unterstreichung das Klick-Signal. */ -a { - color: var(--fg); - text-decoration: underline; +.kreuz { + display: inline-block; + width: 1.1em; + border: 1px solid var(--linie); + background: var(--feld); + font-weight: bold; + text-align: center; } -/* Der Vorgabe-Umriss verschwindet auf dem hellen Bogen fast; im Amtsrot ist der Halt - der Tastatur überall zu sehen. */ -:focus-visible { - outline: 2px solid var(--amt); - outline-offset: 2px; +/* Der Feldblock. Die Rahmen sind halbiert — außen oben und links, an den Zellen unten und + rechts —, damit zwischen zwei Zellen nur eine einzige Haarlinie steht und nicht zwei. */ +.vordruck { + border-top: 1px solid var(--linie); + border-left: 1px solid var(--linie); + background: var(--grund); } -/* Der Feldblock. Die Legende sitzt als schwarzer Balken im oberen Rahmen, wie die - Abschnittsüberschrift eines Vordrucks. */ -fieldset { - counter-reset: feld; - margin: 0 0 1.5rem; - padding: 1.25rem 1.25rem 0.5rem; - border: 1px solid var(--fg); - background: var(--kasten); +.block { + display: grid; + grid-template-columns: 8.5rem 1fr; } -legend { - margin-left: -0.25rem; - padding: 0.3rem 0.8rem; - background: var(--fg); - color: var(--bg); - font-size: 0.8rem; +/* Der Grund steht ausdrücklich an der Zelle und nicht nur am Block: Im Druck fällt sonst + die Fläche des Blocks weg und die Leitspalte käme weiß heraus. */ +.leitspalte { + padding: 0.4rem 0.5rem; + border-right: 1px solid var(--linie); + border-bottom: 1px solid var(--linie); + background: var(--grund); + font-size: 0.6875rem; font-weight: bold; - letter-spacing: 0.08em; - text-transform: uppercase; } -/* Die Feldziffer entsteht aus dem Zähler und steht im Steg links, damit sie die - Beschriftung nicht verschiebt und in jedem Feld an derselben Stelle sitzt. */ -.field { - position: relative; - margin-bottom: 1.25rem; - padding: 0.9rem 1rem 0.9rem 3rem; - border: 1px solid var(--raster); - background: var(--bg); - counter-increment: feld; +/* Auf dem Telefon nähme die Leitspalte die halbe Zeile. Sie tritt dann über ihre Felder, + als voller Beschriftungsstreifen — eine Form, die das Muster ebenfalls kennt. */ +@media (max-width: 34rem) { + .block { + grid-template-columns: 1fr; + } + + .leitspalte { + border-right: none; + } } -.field::before { - content: counter(feld); - position: absolute; - top: 0.85rem; - left: 0.85rem; - width: 1.5rem; - height: 1.5rem; - border: 1px solid var(--amt); - color: var(--amt); - font-size: 0.8rem; - font-weight: bold; - line-height: 1.4rem; - text-align: center; +.feld { + padding: 0.25rem 0.5rem 0.4rem; + border-right: 1px solid var(--linie); + border-bottom: 1px solid var(--linie); + background: var(--feld); } +/* Die Beschriftung steht klein im Feld oben links, die Erläuterung noch kleiner dahinter; + darunter bleibt der Platz für die Eintragung. */ label { display: block; + font-size: 0.6875rem; font-weight: bold; - letter-spacing: 0.04em; - margin-bottom: 0.4rem; } -.field.checkbox label { +.erl { font-weight: normal; - letter-spacing: normal; - margin-bottom: 0; - display: flex; - align-items: center; - gap: 0.5rem; -} - -.field.checkbox input { - accent-color: var(--amt); - width: 1rem; - height: 1rem; - flex: none; -} - -.hint { - margin: 0.4rem 0 0; - font-size: 0.8125rem; + font-size: 0.6875rem; color: var(--muted); } input[type="file"] { width: 100%; - padding: 0.35rem; - border: 1px solid var(--border); + margin-top: 0.35rem; + padding: 0; + border: none; background: transparent; color: inherit; font: inherit; - font-size: 0.875rem; + font-size: 0.75rem; } -/* Der Dateiwähler ist der zweite Knopf des Vordrucks und trägt deshalb dessen Form, - nur in der Umkehrung, damit er dem Absenden nicht den Rang abläuft. */ +/* Der Dateiwähler ist der einzige Knopf im Feld und trägt deshalb die Form der übrigen + Eintragungsfelder: eckig, haarfein umrandet, weiß. */ input[type="file"]::file-selector-button { - margin-right: 0.75rem; - padding: 0.3rem 0.9rem; - border: 1px solid var(--fg); - background: var(--bg); + margin-right: 0.6rem; + padding: 0.15rem 0.7rem; + border: 1px solid var(--linie); + background: var(--feld); color: var(--fg); font: inherit; - font-size: 0.8125rem; - font-weight: bold; - letter-spacing: 0.04em; + font-size: 0.6875rem; cursor: pointer; } input[type="file"]::file-selector-button:hover { - background: var(--fg); - color: var(--bg); + background: var(--grund); +} + +/* Das Ankreuzfeld steht vor dem Text und wird angekreuzt, nicht angehakt. */ +.ankreuzfeld label { + display: flex; + align-items: baseline; + gap: 0.5rem; + font-weight: normal; + font-size: 0.75rem; +} + +.ankreuzfeld input { + appearance: none; + -webkit-appearance: none; + flex: none; + position: relative; + width: 0.95rem; + height: 0.95rem; + margin: 0; + border: 1px solid var(--linie); + background: var(--feld); + cursor: pointer; +} + +.ankreuzfeld input:checked::before { + content: "\00D7"; + position: absolute; + inset: 0; + font-size: 0.9rem; + font-weight: bold; + line-height: 0.85rem; + text-align: center; + color: var(--fg); } .absenden { - margin-bottom: 0.5rem; + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.5rem 0.9rem; + padding-top: 0.5rem; } button { font: inherit; + font-size: 0.8125rem; font-weight: bold; - letter-spacing: 0.08em; - text-transform: uppercase; - padding: 0.7rem 1.75rem; - border: 1px solid var(--fg); - background: var(--fg); - color: var(--bg); - box-shadow: 3px 3px 0 var(--raster); + padding: 0.4rem 1.1rem; + border: 2px solid var(--linie); + background: var(--feld); + color: var(--fg); cursor: pointer; } -/* Umkehr statt brightness(): Ein schwarzer Knopf wird davon nicht heller. */ +/* Umkehr statt brightness(): An einem weißen Knopf richtet die nichts aus. */ button:hover { - background: var(--bg); - color: var(--fg); -} - -/* Gedrückt fährt der Knopf in seinen eigenen Schatten. */ -button:active { - transform: translate(3px, 3px); - box-shadow: none; + background: var(--fg); + color: var(--papier); } /* grayscale() ist an einem grauen Knopf wirkungslos. */ @@ -327,100 +296,52 @@ button[disabled] { cursor: progress; } -/* Hinweiskästen und Meldungen teilen einen Zuschnitt, damit erkennbar bleibt, dass sie - dasselbe sind: Text am Rande des Vordrucks. Der Balken links zeigt den Zustand an. */ -.kasten, -.einfuehrung, -.meldung { - padding: 0.9rem 1.1rem; - border: 1px solid var(--raster); - border-left: 4px solid var(--raster); - background: var(--kasten); - font-size: 0.875rem; -} - -.kasten { - margin: 0 0 1.75rem; -} - -.kasten.warnung { - border-left-color: var(--warnung); +button[disabled]:hover { + background: var(--feld); + color: var(--fg); } -.kasten h2 { - margin: 0 0 0.5rem; - font-size: 0.75rem; +/* Die Verfügung ist im Muster der Abschnitt, den die Stelle ausfüllt: grau umrandet, + mit zentrierter fetter Überschrift. Er steht auch dann schon da, wenn er noch leer ist — + ein Vordruck zeigt seine Abschnitte, bevor sie ausgefüllt sind. */ +.verfuegung { + margin-top: 1.25rem; + border: 1px solid var(--linie); + background: var(--grund); } -.kasten p { +.verfuegung h2 { margin: 0; + padding: 0.3rem 0.5rem; + border-bottom: 1px solid var(--linie); + font-size: 0.75rem; + text-align: center; } -/* Zugeklappt eine Zeile, damit das Formular oben bleibt; aufgeklappt im Zuschnitt der - übrigen Kästen. Der eigene Marker steht vor dem Wort statt daneben, sonst hinge er - außerhalb des Kastenrahmens. */ -.einfuehrung { - margin: 0 0 1.75rem; +.verfuegungsfeld { + padding: 0.5rem; } -.einfuehrung summary { - font-weight: bold; - letter-spacing: 0.04em; - cursor: pointer; - list-style: none; +.platzhalter { + margin: 0; + font-size: 0.75rem; + color: var(--muted); } -.einfuehrung summary::-webkit-details-marker { +/* Steht eine Meldung, so tritt der Platzhalter zurück. */ +#meldung:not([hidden]) + .platzhalter { display: none; } -.einfuehrung summary::before { - content: "\25B8"; - display: inline-block; - width: 1em; - color: var(--amt); -} - -.einfuehrung[open] summary::before { - content: "\25BE"; -} - -.einfuehrung > :not(summary) { - margin: 0.75rem 0 0; -} - -.einfuehrung ol { - padding-left: 1.25rem; -} - -.einfuehrung dt { - font-weight: bold; - margin-top: 0.9rem; - padding-top: 0.5rem; - border-top: 1px solid var(--raster); -} - -.einfuehrung dd { - margin: 0.25rem 0 0; -} - -/* Der Erläuterungssatz unter einem Beispiel steht als span in der dd und braucht deshalb - eigene Absatzbildung. */ -.einfuehrung .hint { - display: block; -} - -.disclaimer { - margin-top: 2rem; - color: var(--muted); -} - -/* Die Meldung führt ihr Zeichen im Balken mit: Wer die Farbe nicht sieht, liest ✓ oder ! - Das Zeichen steht im Stilblatt und nicht im Text, weil app.js den Text ersetzt. - Während der Arbeit steht weder Balken noch Zeichen, weil noch nichts feststeht. */ +/* Die Meldung ist die Eintragung der Stelle und deshalb weiß. Ihr Zeichen im Steg — ✓ oder + ! — trägt den Zustand auch dort, wo die Farbe nicht ankommt. Während der Arbeit steht + weder Zeichen noch Farbe, weil noch nichts feststeht. */ .meldung { - margin: 1.5rem 0 0; - font-size: 0.9375rem; + margin: 0; + padding: 0.45rem 0.6rem; + border: 1px solid var(--linie); + background: var(--feld); + font-size: 0.8125rem; } .meldung.arbeit { @@ -430,13 +351,14 @@ button[disabled] { .meldung.fertig, .meldung.fehler { position: relative; - padding-left: 2.5rem; + padding-left: 2rem; + border-left-width: 4px; } .meldung.fertig::before, .meldung.fehler::before { position: absolute; - left: 1.1rem; + left: 0.7rem; font-weight: bold; } @@ -459,72 +381,134 @@ button[disabled] { color: var(--warnung); } -/* Der Fußsteg trägt die Pflichtangaben und die Herkunft — links das Verzeichnis, rechts - die Quelle, so wie ein Vordruck unten seine Herkunft nennt. */ +/* Die Ausfüllhilfe ist ein Merkblatt und liegt dem Vordruck bei, statt in ihm zu stehen. */ +.ausfuellhilfe { + margin-top: 1.25rem; + padding: 0.5rem 0.7rem; + border: 1px solid var(--linie); + background: var(--tint); + font-size: 0.75rem; +} + +.ausfuellhilfe summary { + font-weight: bold; + cursor: pointer; + list-style: none; +} + +.ausfuellhilfe summary::-webkit-details-marker { + display: none; +} + +.ausfuellhilfe summary::before { + content: "\25B8\00A0"; +} + +.ausfuellhilfe[open] summary::before { + content: "\25BE\00A0"; +} + +.ausfuellhilfe > :not(summary) { + margin: 0.6rem 0 0; +} + +.ausfuellhilfe dt { + margin-top: 0.7rem; + padding-top: 0.5rem; + border-top: 1px solid var(--linie); + font-weight: bold; +} + +.ausfuellhilfe dd { + margin: 0.2rem 0 0; +} + +.ausfuellhilfe .erl { + display: block; +} + +/* Im Muster steht unten links die Vordrucknummer und unten rechts der Ausgabestand, beide + winzig und ohne Zierrat. Die Pflichtangaben stehen dazwischen. */ footer { display: flex; flex-wrap: wrap; + align-items: baseline; justify-content: space-between; - gap: 0.5rem 1.5rem; - margin-top: 2.5rem; - padding-top: 0.9rem; - border-top: 1px solid var(--fg); - font-size: 0.8125rem; - color: var(--muted); -} - -footer a { + gap: 0.4rem 1rem; + margin-top: 1.5rem; + padding: 0.4rem 0 0.6rem; + font-size: 0.625rem; color: var(--muted); } footer nav { - max-width: 32rem; + font-size: 0.6875rem; } -.nebenquelle { +.vordrucknummer, +.ausgabe { white-space: nowrap; } -/* Ein Formblatt gehört ausgedruckt: ohne Unterlage, ohne Bogenrand, mit Linien in - Schwarz. Der Knopf und das Merkblatt entfallen, weil auf Papier weder gedrückt noch - aufgeklappt wird. Gedruckt wird stets auf weißes Papier, auch aus einem dunkel - eingestellten Browser heraus — prefers-color-scheme gilt im Druck fort. Deshalb ist - das ganze Farbgerüst zurückzusetzen und nicht nur ein Teil davon. */ +/* Impressum und Datenschutz: dieselbe Anmutung, aber Fließtext statt Feldblock. */ +.bogen > main { + padding-bottom: 0.5rem; + font-size: 0.8125rem; +} + +main h1 { + font-size: 1rem; +} + +main h2 { + margin: 1.1rem 0 0.3rem; + padding: 0.2rem 0.4rem; + border: 1px solid var(--linie); + background: var(--grund); + font-size: 0.6875rem; +} + +main p, +main address { + margin: 0.4rem 0; + max-width: 42rem; +} + +address { + font-style: normal; +} + +code { + font-size: 0.9375em; +} + +/* Ein Vordruck gehört ausgedruckt: ohne Unterlage, ohne Ränder. Gedruckt wird stets auf + weißes Papier, auch aus einem dunkel eingestellten Browser heraus — prefers-color-scheme + gilt im Druck fort. Deshalb ist das ganze Farbgerüst zurückzusetzen und nicht nur ein + Teil davon. Das Grau selbst muss mitgedruckt werden: Es sagt, welche Felder auszufüllen + sind, und ist damit keine Zier. */ @media print { :root { --fg: #000; - --bg: #fff; --papier: #fff; + --feld: #fff; + --grund: #d5d5d5; + --tint: #e4e4e4; + --linie: #000; --muted: #333; - --border: #666; - --raster: #999; - --kasten: #fff; - --amt: #000; + --tisch: #fff; --warnung: #8b0000; --bestaetigung: #005f00; } - body { - margin: 0; - padding: 0; - max-width: none; - border: none; - font-size: 10pt; - } - - .einfuehrung, - .absenden, - .meldung[hidden] { - display: none; - } - - a { - text-decoration: none; - } + body { padding: 0; font-size: 9pt; } + .bogen { max-width: none; padding: 0; } + .ausfuellhilfe { display: none; } + a { text-decoration: none; } + .block, .beachtung, .verfuegung { break-inside: avoid; } - fieldset, - .field, - .kasten { - break-inside: avoid; + .leitspalte, .ausfuellhinweis, .verfuegung, .verfuegung h2, .kreuz, .ausfuellhilfe { + -webkit-print-color-adjust: exact; + print-color-adjust: exact; } } diff --git a/src/test/java/eu/mulk/aendggner/synopse/HtmlRendererTest.java b/src/test/java/eu/mulk/aendggner/synopse/HtmlRendererTest.java index 09a5b7c..b80fc88 100644 --- a/src/test/java/eu/mulk/aendggner/synopse/HtmlRendererTest.java +++ b/src/test/java/eu/mulk/aendggner/synopse/HtmlRendererTest.java @@ -108,20 +108,22 @@ class HtmlRendererTest { } @Test - void traegtKennungUndFusssteg() { + void traegtVorspannUndFusssteg() { var html = HtmlRenderer.rendere(synopse(), "test.xml + test.pdf"); - assertThat(html).contains("‑2").contains("Stammgesetz").contains("nichtamtlich"); - assertThat(html).contains("Grundlage: test.xml + test.pdf"); + assertThat(html).contains("<dt>Stammgesetz</dt><dd>TestG</dd>"); + assertThat(html).contains("<dt>Grundlage</dt><dd>test.xml + test.pdf</dd>"); + assertThat(html).contains("<dt>Geltung</dt><dd>nichtamtlich</dd>"); + assertThat(html).contains("class=\"vordrucknummer\">ÄG‑2<"); assertThat(html).contains("maßgeblich ist allein die amtliche Verkündung"); - assertThat(html).doesNotContain("Entwurfsfassung"); + assertThat(html).doesNotContain("Entwurfsstand"); } @Test - void weistDieEntwurfsfassungInDerKennungAus() { + void weistDenEntwurfsstandImVorspannAus() { var html = HtmlRenderer.rendere(synopse(), "quelle", true); - assertThat(html).contains("<dt>Stand</dt><dd>Entwurfsfassung</dd>"); + assertThat(html).contains("<dt>Geltung</dt><dd>nichtamtlich, Entwurfsstand</dd>"); assertThat(html).contains("nicht geltendes Recht"); } |
