/* GTIN Generator – bewusst klein, hell und scanfreundlich. */ :root { --schwarz: #111111; --text: #111111; --text-leise: #4b5563; --linie: #d1d5db; --flaeche: #ffffff; --hintergrund: #f4f5f7; --fokus: #1d4ed8; --radius: 12px; --abstand: 16px; } *, *::before, *::after { box-sizing: border-box; } html { -webkit-text-size-adjust: 100%; } body { margin: 0; min-height: 100vh; background: var(--hintergrund); color: var(--text); font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; font-size: 1rem; line-height: 1.5; } body.scan-offen { overflow: hidden; } body.liste-offen { overflow: hidden; } [hidden] { display: none !important; } :focus-visible { outline: 3px solid var(--fokus); outline-offset: 2px; } .app { width: 100%; max-width: 44rem; margin: 0 auto; padding: calc(12px + env(safe-area-inset-top)) var(--abstand) calc(var(--abstand) + env(safe-area-inset-bottom)); display: flex; flex-direction: column; gap: 12px; } /* Fuer Hilfstechnik vorhanden, ohne Platz auf dem Bildschirm zu kosten. */ .nur-vorlesen { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0; } .hinweis-badge { display: block; margin: 0; padding: 6px 10px; border-radius: var(--radius); background: #fef3c7; color: #92400e; font-size: 0.85rem; text-align: center; } .karte { background: var(--flaeche); border: 1px solid var(--linie); border-radius: var(--radius); padding: var(--abstand); } .feld-titel { margin: 0 0 8px; font-size: 1rem; font-weight: 600; } .feld { width: 100%; min-height: 3.5rem; padding: 12px 14px; border: 2px solid var(--linie); border-radius: var(--radius); background: var(--flaeche); color: var(--text); font-size: 1.5rem; font-variant-numeric: tabular-nums; letter-spacing: 0.08em; text-align: center; } .feld:focus { border-color: var(--schwarz); } .feld-hinweis { margin: 8px 0 0; color: var(--text-leise); font-size: 0.9rem; text-align: center; } .fehler { margin: 10px 0 0; padding: 10px 12px; border-radius: var(--radius); background: #fee2e2; color: #991b1b; font-weight: 600; text-align: center; } .ergebnis { text-align: center; } .gtin-typ { margin: 0; color: var(--text-leise); font-size: 0.85rem; letter-spacing: 0.18em; text-transform: uppercase; } .gtin { margin: 6px 0 14px; padding: 6px 10px; border: 0; border-radius: var(--radius); background: none; color: var(--text); font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: clamp(1.9rem, 9vw, 2.75rem); font-weight: 700; letter-spacing: 0.06em; font-variant-numeric: tabular-nums; cursor: pointer; } .barcode { display: flex; justify-content: center; padding: 10px 4px; background: var(--flaeche); border-radius: var(--radius); } .barcode svg { display: block; max-width: 100%; height: auto; } .aktionen { margin-top: var(--abstand); display: flex; flex-direction: column; gap: 10px; } .aktionen-reihe { display: flex; gap: 10px; } .aktionen-reihe .knopf { flex: 1 1 0; min-width: 0; } .knopf { min-height: 3rem; padding: 12px 16px; border: 2px solid var(--schwarz); border-radius: var(--radius); background: var(--flaeche); color: var(--text); font-size: 1rem; font-weight: 600; cursor: pointer; } .knopf-stark { background: var(--schwarz); color: #ffffff; } .knopf-leise { border-color: transparent; background: none; color: var(--text-leise); text-decoration: underline; font-weight: 500; } .knopf:hover { opacity: 0.9; } .verlauf-titel { margin: 0 0 10px; font-size: 1rem; font-weight: 600; } .verlauf-liste { margin: 0 0 10px; padding: 0; list-style: none; display: flex; flex-direction: column; } .verlauf-liste li + li { border-top: 1px solid var(--linie); } .verlauf-eintrag { width: 100%; min-height: 3rem; display: flex; align-items: center; gap: 10px; padding: 10px 8px; border: 0; border-radius: 8px; background: none; color: var(--text); font-size: 1rem; font-variant-numeric: tabular-nums; text-align: left; cursor: pointer; touch-action: manipulation; } .verlauf-eintrag:hover, .verlauf-eintrag:focus-visible { background: #eef2ff; } .verlauf-artikel { min-width: 5.5rem; font-weight: 600; } .verlauf-pfeil { color: var(--text-leise); } .verlauf-gtin { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; letter-spacing: 0.04em; } .fuss { display: flex; flex-direction: column; align-items: center; gap: 8px; padding-bottom: 8px; } .fuss-hinweis { margin: 0; color: var(--text-leise); font-size: 0.9rem; text-align: center; } .toast { position: fixed; left: 50%; bottom: calc(20px + env(safe-area-inset-bottom)); transform: translateX(-50%); margin: 0; padding: 10px 18px; border-radius: 999px; background: var(--schwarz); color: #ffffff; font-weight: 600; z-index: 60; } .kopier-hilfe { position: fixed; top: -1000px; left: -1000px; opacity: 0; } /* ------------------------------------------------------------ Scanmodus -- */ .scan { position: fixed; inset: 0; z-index: 50; background: #ffffff; display: flex; align-items: center; justify-content: center; padding: calc(12px + env(safe-area-inset-top)) 12px calc(12px + env(safe-area-inset-bottom)); } .scan-innen { width: 100%; height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 14px; outline: none; } .scan-seite { display: flex; flex-direction: column; align-items: center; gap: 12px; } .scan-barcode { width: 100%; /* Nimmt im Hochformat die verbleibende Hoehe ein, damit der Barcode so gross wie moeglich wird. Die Groesse wird in app.js gemessen. */ flex: 1 1 auto; min-height: 0; display: flex; align-items: center; justify-content: center; } .scan-barcode svg { display: block; max-width: 100%; max-height: 100%; height: auto; } .scan-gtin { margin: 0; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: clamp(1.5rem, 7vw, 2.5rem); font-weight: 700; letter-spacing: 0.08em; font-variant-numeric: tabular-nums; } .scan-hinweis { margin: 0; color: var(--text-leise); font-size: 0.9rem; text-align: center; } .scan-zurueck { min-width: 12rem; } /* * Querformat: Der Barcode bekommt die volle Breite des Bildschirms, GTIN und * Zurueck-Schaltflaeche stehen daneben. Dadurch wird der grosse Code deutlich * groesser als im Hochformat, wo die Beschriftung darunter liegt. */ @media (orientation: landscape) { .scan-innen { flex-direction: row; gap: 18px; } .scan-barcode { width: auto; align-self: stretch; } .scan-seite { flex: 0 1 auto; min-width: 0; max-width: 34%; align-self: center; gap: 10px; } .scan-gtin { font-size: clamp(1rem, 4.4vh, 1.9rem); } .scan-hinweis { font-size: 0.8rem; } .scan-zurueck { width: 100%; min-width: 0; } } /* ---------------------------------------------------------- Listenmodus -- */ /* Der Einstieg ist bewusst ein kleiner Link unter dem Werkzeug: Die Einzelansicht bleibt die Hauptansicht. */ .fuss-liste { margin: 0; } .fuss-datenschutz { margin: 0; color: var(--text-leise); font-size: 0.78rem; text-align: center; } .fuss-datenschutz a, .fuss-rechtliches a { color: inherit; text-underline-offset: 0.18em; } .fuss-rechtliches { display: flex; flex-wrap: wrap; justify-content: center; gap: 0.35rem 0.9rem; font-size: 0.78rem; } .link-klein { min-height: 2.75rem; padding: 6px 10px; border: 0; border-radius: 8px; background: none; color: var(--text-leise); font-size: 0.9rem; font-weight: 500; text-decoration: underline; cursor: pointer; } .link-klein:hover { color: var(--text); } .liste { position: fixed; inset: 0; z-index: 50; background: var(--flaeche); padding: calc(12px + env(safe-area-inset-top)) var(--abstand) calc(12px + env(safe-area-inset-bottom)); } .liste-innen { width: 100%; max-width: 44rem; height: 100%; margin: 0 auto; display: flex; flex-direction: column; gap: 10px; outline: none; } .liste-erfassen, .liste-anzeige { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; gap: 10px; } /* Schritt 1: Nummern erfassen. */ .liste-form { display: flex; flex-direction: column; gap: 8px; } /* Die Abstaende liegen am Formular, nicht an den einzelnen Elementen. */ .liste-form .feld-titel, .liste-form .feld-hinweis, .liste-form .fehler { margin: 0; } /* * Feld und Bestaetigung bilden eine Zeile: Der Knopf sitzt unmittelbar neben * der Eingabe, damit das Bestaetigen auf dem Telefon nicht von der * Bildschirmtastatur abhaengt. Auf sehr schmalen Anzeigen bricht er unter das * Feld und bleibt dort ein volles Touchziel. */ .liste-eingabezeile { display: flex; flex-wrap: wrap; gap: 8px; } .liste-eingabezeile .feld { flex: 1 1 9rem; width: auto; min-width: 0; } .liste-hinzufuegen { flex: 1 1 auto; } .liste-titel { margin: 0; font-size: 1rem; font-weight: 600; } .liste-anzahl { display: inline-block; min-width: 1.6rem; margin-left: 6px; padding: 1px 7px; border-radius: 999px; background: var(--schwarz); color: #ffffff; font-size: 0.8rem; font-variant-numeric: tabular-nums; text-align: center; } .liste-eintraege { flex: 1 1 auto; min-height: 0; overflow-y: auto; margin: 0; padding: 0; list-style: none; } .liste-eintrag { display: flex; align-items: center; gap: 10px; border-top: 1px solid var(--linie); } .liste-eintrag:first-child { border-top: 0; } .liste-nr { min-width: 5.5rem; font-weight: 600; font-variant-numeric: tabular-nums; } .liste-eintrag .liste-gtin { font-size: 1rem; font-weight: 400; letter-spacing: 0.04em; } .liste-weg { margin-left: auto; min-width: 2.75rem; min-height: 2.75rem; border: 0; border-radius: 8px; background: none; color: var(--text-leise); font-size: 1.1rem; cursor: pointer; } .liste-weg:hover, .liste-weg:focus-visible { background: var(--hintergrund); color: var(--text); } .liste-leer { margin: 0; padding: 14px 0; color: var(--text-leise); text-align: center; } .liste-aktionen, .liste-links { display: flex; flex-direction: column; align-items: center; gap: 6px; } /* Schritt 2: GTIN und Barcode nacheinander anzeigen. */ .liste-zaehler { margin: 0; color: var(--text-leise); font-size: 0.9rem; font-variant-numeric: tabular-nums; text-align: center; } .liste-buehne { flex: 1 1 auto; min-height: 0; display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: minmax(0, 1fr) auto; grid-template-areas: "mitte mitte" "zurueck vor"; gap: 10px; touch-action: pan-y; } .liste-mitte { grid-area: mitte; min-height: 0; display: flex; flex-direction: column; align-items: center; gap: 8px; outline: none; } .liste-barcode { flex: 1 1 auto; min-height: 0; width: 100%; display: flex; align-items: center; justify-content: center; } .liste-barcode svg { display: block; max-width: 100%; max-height: 100%; height: auto; } .liste-gtin { margin: 0; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: clamp(1.4rem, 6.5vw, 2.2rem); font-weight: 700; letter-spacing: 0.07em; font-variant-numeric: tabular-nums; } .liste-pfeil { min-height: 3rem; border: 2px solid var(--schwarz); border-radius: var(--radius); background: var(--flaeche); color: var(--text); font-size: 1.25rem; cursor: pointer; } #liste-zurueck { grid-area: zurueck; } #liste-vor { grid-area: vor; } .liste-artikel { margin: 0; color: var(--text-leise); font-variant-numeric: tabular-nums; text-align: center; } /* * Der Schritt zur naechsten Nummer zeigt die Richtung: Der neue Barcode kommt * von der Seite, in die der Nutzer geblättert hat. 160 ms, einmalig, und unter * "prefers-reduced-motion: reduce" abgeschaltet. */ @keyframes liste-schritt-vor { from { transform: translateX(14px); opacity: 0.35; } to { transform: none; opacity: 1; } } @keyframes liste-schritt-zurueck { from { transform: translateX(-14px); opacity: 0.35; } to { transform: none; opacity: 1; } } .liste-schritt-vor { animation: liste-schritt-vor 160ms ease-out; } .liste-schritt-zurueck { animation: liste-schritt-zurueck 160ms ease-out; } /* * Querformat: Die Pfeile stehen als Leisten neben dem Barcode, der dadurch die * volle Hoehe bekommt. */ @media (orientation: landscape) { .liste-buehne { grid-template-columns: auto minmax(0, 1fr) auto; grid-template-rows: minmax(0, 1fr); grid-template-areas: "zurueck mitte vor"; } .liste-pfeil { width: 4rem; height: 100%; } } @media (min-width: 40rem) { .aktionen { flex-direction: row-reverse; } .aktionen .knopf-stark { flex: 1 1 0; } .aktionen-reihe { flex: 1 1 0; } } @media (prefers-reduced-motion: reduce) { * { transition: none !important; animation: none !important; } }