From 4e5d1e32e8dfb3f1d949c0b864c5726daa40b9c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathias=20Gla=CC=88ser?= Date: Tue, 8 Sep 2026 11:19:32 +0200 Subject: [PATCH] feat: add file upload pattern --- components/interactive-elements.html | 20 +++++ index.html | 1 + patterns/file-upload.html | 75 ++++++++++++++++ semantic-tokens-patterns.html | 8 ++ styleguide.css | 1 + styles/10-components-interactive-elements.css | 42 +++++++++ styles/47-patterns-file-upload.css | 86 +++++++++++++++++++ 7 files changed, 233 insertions(+) create mode 100644 patterns/file-upload.html create mode 100644 styles/47-patterns-file-upload.css diff --git a/components/interactive-elements.html b/components/interactive-elements.html index 7fd70a8..5625d64 100644 --- a/components/interactive-elements.html +++ b/components/interactive-elements.html @@ -847,7 +847,27 @@ +
+

Component: File Input

+
+
+ +

Mehrere Dateien können ausgewählt werden.

+ + +
+
+
+ + + diff --git a/semantic-tokens-patterns.html b/semantic-tokens-patterns.html index 249e034..83b8520 100644 --- a/semantic-tokens-patterns.html +++ b/semantic-tokens-patterns.html @@ -148,6 +148,14 @@ layout-delete-confirmation-target-dim-opacityopacity-delete-confirmation-target-dimDeckkraft der grauen Überlagerung auf dem zu löschenden Objekt. layer-delete-confirmation-overlaydimension-layer-delete-confirmation-overlayZ-Index-Layer des schwebenden Bestätigungsfensters. + +

Datei-Upload

+ + + + + +
Semantischer TokenVerwendeter Foundation-TokenBeschreibung Verwendungszweck im Pattern
surface-file-upload-dropzonesurface-form-previewHintergrundfläche des auswählbaren Upload-Bereichs.
surface-file-upload-file-itemsurface-control-defaultHintergrundfläche eines Eintrags in der Dateiliste.
text-file-upload-metatext-control-disabledTextfarbe für Dateigröße und ergänzende Dateimetadaten.
text-file-upload-errorcolor-signal-redTextfarbe für Validierungs- und Upload-Fehlermeldungen.
diff --git a/styleguide.css b/styleguide.css index 61459dc..376890b 100644 --- a/styleguide.css +++ b/styleguide.css @@ -2,6 +2,7 @@ @import "./styles/02-base.css"; @import "./styles/03-typography-helpers.css"; @import "./styles/10-components-interactive-elements.css"; +@import "./styles/47-patterns-file-upload.css"; @import "./styles/20-patterns-portal-header.css"; @import "./styles/21-patterns-options-row.css"; @import "./styles/22-patterns-object-card.css"; diff --git a/styles/10-components-interactive-elements.css b/styles/10-components-interactive-elements.css index 91b280b..d064e22 100644 --- a/styles/10-components-interactive-elements.css +++ b/styles/10-components-interactive-elements.css @@ -1585,3 +1585,45 @@ color: var(--text-hyperlink); text-decoration: none; } + +/* Component: File Input */ +.sg-file-input { + display: flex; + flex-direction: column; + gap: var(--spacing-small); + width: 100%; +} + +.sg-file-input__control { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} + +.sg-file-input:focus-within .sg-file-input__trigger { + outline: 2px solid var(--text-hyperlink); + outline-offset: 2px; +} + +.sg-file-input__description { + margin: 0; + color: var(--text-control-disabled); +} + +.sg-file-input__trigger { + align-self: flex-start; +} + +.sg-file-input--disabled { + opacity: var(--disabled-opacity); +} + +.sg-file-input--disabled .sg-file-input__trigger { + cursor: not-allowed; +} diff --git a/styles/47-patterns-file-upload.css b/styles/47-patterns-file-upload.css new file mode 100644 index 0000000..2af081a --- /dev/null +++ b/styles/47-patterns-file-upload.css @@ -0,0 +1,86 @@ +/* ========================================================= */ +/* Patterns: Datei-Upload */ +/* ========================================================= */ + +.sg-file-upload { + --surface-file-upload-dropzone: var(--surface-form-preview); + --surface-file-upload-file-item: var(--surface-control-default); + --text-file-upload-meta: var(--text-control-disabled); + --text-file-upload-error: var(--color-signal-red); + display: flex; + flex-direction: column; + gap: var(--spacing-large); + width: 100%; + max-width: var(--layout-input-field-max-width); +} + +.sg-file-upload__description, +.sg-file-upload__status, +.sg-file-upload__file-meta { + margin: var(--spacing-small) 0 0; +} + +.sg-file-upload__dropzone { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: var(--spacing-small); + padding: var(--spacing-large); + background: var(--surface-file-upload-dropzone); + border: 1px dashed var(--surface-button-active); + border-radius: var(--radius-interaction); +} + +.sg-file-upload__dropzone[data-drag-over="true"] { + outline: 2px solid var(--text-hyperlink); + outline-offset: 2px; +} + +.sg-file-upload__prompt { + margin: 0; +} + +.sg-file-upload__files { + display: flex; + flex-direction: column; + gap: var(--spacing-small); + margin: 0; + padding: 0; + list-style: none; +} + +.sg-file-upload__file { + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--spacing-large); + padding: var(--spacing-small) var(--spacing-large); + color: var(--text-control-default); + background: var(--surface-file-upload-file-item); + border-radius: var(--radius-interaction); +} + +.sg-file-upload__file-name { + min-width: 0; + overflow-wrap: anywhere; +} + +.sg-file-upload__file-meta { + color: var(--text-file-upload-meta); +} + +.sg-file-upload__remove { + flex: 0 0 auto; +} + +.sg-file-upload__error { + color: var(--text-file-upload-error); +} + +@media (max-width: 48rem) { + .sg-file-upload__file { + align-items: flex-start; + flex-direction: column; + gap: var(--spacing-small); + } +}