UNPKG

@casoon/dragonfly

Version:

Modular, lightweight CSS framework and design system for modern web projects. Optimized for Astro JS, LightningCSS and Container Queries with @layer-based architecture and comprehensive accessibility.

1,789 lines (1,526 loc) 76 kB
/* * Forms Module - Complete Form System * * Comprehensive form elements and controls for user input and interaction. * This module combines all form-related components and provides both simple * utility classes and advanced form system features. * * CONTENTS: * * 1. BASIC FORM ELEMENTS (Simple utility classes): * - Buttons (interactive form controls) * - Input fields (text, email, password, etc.) * - Textarea (multi-line text input) * - Select dropdowns (single and multiple selection) * - Checkboxes (multiple selection) * - Radio buttons (single selection from group) * - Switches (toggle controls) * - Sliders (range input controls) * * 2. ADVANCED FORM SYSTEM (Bootstrap-style components): * - Form containers and layouts (.form, .form-group, .form-row) * - Form controls (.form-control, .form-select, .form-check) * - Labels and feedback (.form-label, .form-text, .feedback) * - Validation states and feedback mechanisms * - Input groups (.input-group, .input-group-text) * - Responsive and adaptive layouts * - RTL support and accessibility features * * @layer components.forms * * Accessibility Features: * - Semantic HTML elements as foundation * - Proper focus management and keyboard navigation * - ARIA attributes where needed * - High contrast focus indicators * - Screen reader friendly labels and descriptions */ /** * Form Elements Collection * * This file contains all form-related components: * - Buttons (interactive form controls) * - Input fields (text, email, password, etc.) * - Textarea (multi-line text input) * - Select dropdowns (single and multiple selection) * - Checkboxes (multiple selection) * - Radio buttons (single selection from group) * - Switches (toggle controls) * - Sliders (range input controls) * * @layer components.forms * * Accessibility Features: * - Semantic HTML elements as foundation * - Proper focus management and keyboard navigation * - ARIA attributes where needed * - High contrast focus indicators * - Screen reader friendly labels and descriptions */ /* =================== ANIMATIONS =================== */ /* =================== BUTTONS =================== */ @layer components { /* Base Button Style */ .button { align-items: center; border: none; border-radius: var(--radius-md, 0.5rem); cursor: pointer; display: inline-flex; font-size: var(--font-size-sm, 0.875rem); font-weight: var(--font-weight-medium, 500); justify-content: center; line-height: 1.25; padding-block: var(--space-2, 0.5rem); padding-inline: var(--space-4, 1rem); position: relative; text-decoration: none; transition: background-color var(--transition-duration-fast, 150ms) var(--transition-timing-ease, cubic-bezier(0.4, 0, 0.2, 1)); } /* Button Variants */ .button--primary { background-color: var(--color-primary, #06c); color: white; } .button--primary:hover { background-color: var(--color-primary-hover, #05a); } .button--secondary { background-color: var(--color-secondary); color: white; } .button--secondary:hover { background-color: var(--color-secondary-hover); } .button--tertiary { background-color: transparent; border: 1px solid var(--color-border, #e5e7eb); color: var(--color-text); } .button--tertiary:hover { background-color: var(--color-background-alt, #f9fafb); } .button--success { background-color: var(--color-success, #10b981); color: white; } .button--success:hover { background-color: var(--color-success-hover); } .button--warning { background-color: var(--color-warning, #f59e0b); color: var(--color-text); } .button--warning:hover { background-color: var(--color-warning-hover); } .button--danger { background-color: var(--color-error, #ef4444); color: white; } .button--danger:hover { background-color: var(--color-error-hover); } .button--info { background-color: var(--color-info, #3b82f6); color: white; } .button--info:hover { background-color: var(--color-info-hover); } .button--link { background-color: transparent; color: var(--color-primary, #3b82f6); padding: 0; text-decoration: underline; } .button--link:hover { color: var(--color-primary-hover, #2563eb); text-decoration: none; } /* Button Sizes */ .button--xs { font-size: var(--font-size-xs); padding-block: var(--space-1); padding-inline: var(--space-2); } .button--sm { font-size: var(--font-size-sm, 0.875rem); padding-block: var(--space-1); padding-inline: var(--space-3); } .button--md { font-size: var(--font-size-base, 1rem); padding-block: var(--space-2); padding-inline: var(--space-4); } .button--lg { font-size: var(--font-size-lg); padding-block: var(--space-3); padding-inline: var(--space-6); } .button--xl { font-size: var(--font-size-xl); padding-block: var(--space-4); padding-inline: var(--space-8); } /* Button States */ button:disabled, .button:disabled { cursor: not-allowed; opacity: var(--opacity-50); } .button--loading { color: transparent; pointer-events: none; } .button--with-icon { gap: var(--space-2); } .button--with-icon .icon { flex-shrink: 0; height: 1em; width: 1em; } .button--icon-only { aspect-ratio: 1; padding: var(--space-2); } .button--icon-only .icon { height: 1.25em; width: 1.25em; } .button--loading::after { animation: var(--ui-spin-animation, spin var(--animation-duration-slowest, 1000ms) var(--ease-linear, linear) infinite); border: 2px solid transparent; border-radius: var(--radius-full, 9999px); border-top-color: currentColor; content: ""; height: 1em; left: 50%; position: absolute; top: 50%; transform: translate(-50%, -50%); width: 1em; } } /* =================== INPUT FIELDS =================== */ @layer components { .input { background-color: var(--color-white, #fff); border: 1px solid var(--color-gray-300, #d1d5db); border-radius: var(--radius-md, 0.375rem); color: var(--color-gray-900, #111827); display: block; font-size: var(--font-size-base, 1rem); padding: var(--space-2) var(--space-3); transition: border-color var(--transition-fast, 150ms ease), background-color var(--transition-fast, 150ms ease); width: 100%; &:focus { border-color: var(--color-primary, #3b82f6); box-shadow: 0 0 0 2px color-mix(in srgb, var(--color-primary, #3b82f6) 50, transparent); outline: none; } &.invalid { background-color: color-mix(in srgb, var(--color-error, #ef4444) 5%, white); border-color: var(--color-error, #ef4444); } &:disabled { background-color: var(--color-gray-100, #f3f4f6); color: var(--color-gray-500); cursor: not-allowed; } &:read-only { background-color: var(--color-gray-50); } /* Input Variants */ &.primary { border-color: var(--color-primary, #3b82f6); } &.success { border-color: var(--color-success, #10b981); } &.warning { border-color: var(--color-warning, #f59e0b); } &.danger { border-color: var(--color-danger); } /* Input Sizes */ &.sm { font-size: var(--font-size-sm, 0.875rem); padding: var(--space-1) var(--space-2); } &.lg { font-size: var(--font-size-lg); padding: var(--space-3) var(--space-4); } } /* Input with Icon */ .input-wrapper { align-items: center; display: flex; position: relative; .input { padding-left: var(--space-8); } .input-icon { color: var(--color-gray-400); left: var(--space-3); pointer-events: none; position: absolute; } } } /* =================== TEXTAREA =================== */ @layer components { .textarea { background-color: var(--color-white, #fff); border: 1px solid var(--color-gray-300, #d1d5db); border-radius: var(--radius-md, 0.375rem); color: var(--color-gray-900, #111827); font-size: var(--font-size-base, 1rem); min-height: 6rem; padding: var(--space-3); resize: vertical; transition: border-color var(--transition-fast, 150ms ease), box-shadow var(--transition-fast, 150ms ease); width: 100%; &:focus { border-color: var(--color-primary, #3b82f6); box-shadow: 0 0 0 2px color-mix(in srgb, var(--color-primary, #3b82f6) 50, transparent); outline: none; } &:disabled { background-color: var(--color-gray-100, #f3f4f6); color: var(--color-gray-500); cursor: not-allowed; opacity: 0.7; } /* Textarea Sizes */ &.sm { font-size: var(--font-size-sm, 0.875rem); min-height: 4rem; padding: var(--space-2); } &.lg { font-size: var(--font-size-lg); min-height: 8rem; padding: var(--space-4); } /* Textarea States */ &.error { background-color: color-mix(in srgb, var(--color-error, #ef4444) 3%, var(--color-white, #fff)); border-color: var(--color-error, #ef4444); &:focus { box-shadow: 0 0 0 2px color-mix(in srgb, var(--color-error, #ef4444) 30, transparent); } } &.success { background-color: color-mix(in srgb, var(--color-success, #10b981) 3%, var(--color-white, #fff)); border-color: var(--color-success, #10b981); &:focus { box-shadow: 0 0 0 2px color-mix(in srgb, var(--color-success, #10b981) 30, transparent); } } &.readonly { background-color: var(--color-gray-50); cursor: default; } &.auto-resize { overflow-y: hidden; } } /* Textarea with Counter */ .textarea-container { position: relative; width: 100%; .counter { bottom: var(--space-2); color: var(--color-gray-500); font-size: var(--font-size-xs); pointer-events: none; position: absolute; right: var(--space-3); } .textarea { padding-bottom: calc(var(--space-3) + 1.5rem); } } } /* =================== SELECT DROPDOWNS =================== */ @layer components { .select { appearance: none; background-color: var(--color-white, #fff); background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 20 20' fill='none' stroke='%23666' stroke-width='2' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M6 8l4 4 4-4'/%3E%3C/svg%3E"); background-position: right var(--space-3) center; background-repeat: no-repeat; background-size: 1rem; border: 1px solid var(--color-gray-300, #d1d5db); border-radius: var(--radius-md, 0.375rem); color: var(--color-gray-900, #111827); cursor: pointer; display: block; font-size: var(--font-size-base, 1rem); padding: var(--space-2) var(--space-3); transition: border-color var(--transition-fast, 150ms ease), box-shadow var(--transition-fast, 150ms ease); width: 100%; &:focus { border-color: var(--color-primary, #3b82f6); box-shadow: 0 0 0 2px color-mix(in srgb, var(--color-primary, #3b82f6) 50, transparent); outline: none; } &:disabled { background-color: var(--color-gray-100, #f3f4f6); color: var(--color-gray-500); cursor: not-allowed; opacity: 0.7; } /* Select Sizes */ &.sm { background-position: right var(--space-2) center; background-size: 0%.875rem; font-size: var(--font-size-sm, 0.875rem); padding: var(--space-1) var(--space-2); } &.lg { background-position: right var(--space-4) center; background-size: 1%.25rem; font-size: var(--font-size-lg); padding: var(--space-3) var(--space-4); } /* Select States */ &.error { background-color: color-mix(in srgb, var(--color-error, #ef4444) 3%, var(--color-white, #fff)); border-color: var(--color-error, #ef4444); &:focus { box-shadow: 0 0 0 2px color-mix(in srgb, var(--color-error, #ef4444) 30, transparent); } } &.success { background-color: color-mix(in srgb, var(--color-success, #10b981) 3%, var(--color-white, #fff)); border-color: var(--color-success, #10b981); &:focus { box-shadow: 0 0 0 2px color-mix(in srgb, var(--color-success, #10b981) 30, transparent); } } /* Multiple Selection */ &[multiple] { background-image: none; padding: var(--space-1); option { border-radius: var(--radius-sm, 0.125rem); padding: var(--space-1) var(--space-2); &:checked { background-color: color-mix(in srgb, var(--color-primary, #3b82f6) 15%, transparent); } } } /* Option Groups */ optgroup { color: var(--color-gray-700); font-weight: var(--font-weight-medium, 500); } } } /* =================== CHECKBOXES =================== */ @layer elements { .checkbox { align-items: center; cursor: pointer; display: inline-flex; font-size: var(--text-sm, 0.875rem); gap: var(--space-2); position: relative; } .checkbox .input { appearance: none; background-color: white; border: 1px solid var(--color-neutral-400, #9ca3af); border-radius: var(--radius-sm, 0.125rem); cursor: pointer; height: 1rem; position: relative; transition: all 0.2s; width: 1rem; } .checkbox .input:checked { background-color: var(--color-primary-500); border-color: var(--color-primary-500); } .checkbox .input:checked::after { border: solid white; border-width: 0% 2px 2px 0; content: ""; height: 6px; left: 4px; position: absolute; top: 1px; transform: rotate(45deg); width: 3px; } .checkbox .label { color: var(--color-text-700, #374151); font-size: var(--text-sm, 0.875rem); } .checkbox .input:focus-visible { outline: 2px solid var(--color-primary-300); outline-offset: 2px; } .checkbox .input:hover:not(:disabled) { border-color: var(--color-primary-500); } .checkbox .input:disabled { background-color: var(--color-neutral-100, #f3f4f6); border-color: var(--color-neutral-300, #d1d5db); cursor: not-allowed; opacity: var(--opacity-50); } .checkbox .input:disabled ~ .checkbox .label { color: var(--color-text-400); cursor: not-allowed; } /* Checkbox Sizes */ .checkbox--sm .input { height: 0%.875rem; width: 0%.875rem; } .checkbox--sm .input:checked::after { height: 5px; left: 3px; top: 1px; width: 2px; } .checkbox--sm .label { font-size: var(--text-xs, 0.75rem); } .checkbox--lg .input { height: 1%.25rem; width: 1%.25rem; } .checkbox--lg .input:checked::after { height: 8px; left: 5px; top: 2px; width: 4px; } .checkbox--lg .label { font-size: var(--text-base); } /* Checkbox Groups */ .checkbox-group { display: flex; flex-direction: column; gap: var(--space-3); } .checkbox-group--horizontal { flex-flow: row wrap; gap: var(--space-4); } } /* =================== RADIO BUTTONS =================== */ @layer components { .radio { align-items: center; cursor: pointer; display: inline-flex; font-size: var(--font-size-sm, 0.875rem); gap: var(--space-2); input[type="radio"] { appearance: none; background-color: var(--color-white, #fff); border: 2px solid var(--color-gray-400); border-radius: 9px; height: 1rem; position: relative; transition: all var(--transition-fast, 150ms ease); width: 1rem; &:checked { background-color: var(--color-primary, #3b82f6); border-color: var(--color-primary, #3b82f6); &::after { background-color: white; border-radius: 9px; content: ""; height: 0%.375rem; left: 50%; position: absolute; top: 50%; transform: translate(-50%, -50%); width: 0%.375rem; } } &:focus-visible { outline: 2px solid var(--color-primary, #3b82f6); outline-offset: 2px; } &:disabled { background-color: var(--color-gray-100, #f3f4f6); border-color: var(--color-gray-300, #d1d5db); cursor: not-allowed; & ~ .label { color: var(--color-gray-400); cursor: not-allowed; } } } /* Radio Sizes */ &.sm { font-size: var(--font-size-xs); input[type="radio"] { height: 0%.875rem; width: 0%.875rem; } } &.lg { font-size: var(--font-size-base, 1rem); input[type="radio"] { height: 1%.25rem; width: 1%.25rem; &:checked::after { height: 0%.5rem; width: 0%.5rem; } } } } .radio-group { display: flex; flex-direction: column; gap: var(--space-2); &.horizontal { align-items: center; flex-flow: row wrap; } } } /* =================== SWITCHES =================== */ @layer elements { .switch { align-items: center; cursor: pointer; display: inline-flex; gap: var(--space-2); position: relative; } .switch .input { height: 0%; opacity: 0%; position: absolute; width: 0%; } .switch .track { background-color: var(--color-neutral-300, #d1d5db); border-radius: var(--radius-full, 9999px); display: inline-block; height: var(--switch-height, 1.25rem); position: relative; transition: background-color 0.2s; width: var(--switch-width, 2.5rem); } .switch .thumb { background-color: white; border-radius: var(--radius-full, 9999px); box-shadow: 0 1px 2px rgb(0 0 0 / 1000%); height: calc(var(--switch-height, 1.25rem) * 0.8); left: calc(var(--switch-height, 1.25rem) * 0.1); position: absolute; top: calc(var(--switch-height, 1.25rem) * 0.1); transition: transform 0.2s; width: calc(var(--switch-height, 1.25rem) * 0.8); } .switch .label { color: var(--color-text-700, #374151); font-size: var(--text-sm, 0.875rem); } .switch .input:checked + .switch .track { background-color: var(--color-primary-500); } .switch .input:checked + .switch .track .switch .thumb { transform: translateX(calc(var(--switch-width, 2.5rem) - var(--switch-height, 1.25rem))); } .switch .input:focus-visible + .switch .track { outline: 2px solid var(--color-primary-500); outline-offset: 2px; } .switch .input:disabled + .switch .track { background-color: var(--color-neutral-200, #e5e7eb); cursor: not-allowed; opacity: 0.7; } .switch .input:disabled + .switch .track .switch .thumb { background-color: var(--color-neutral-400, #9ca3af); } .switch .input:disabled ~ .switch .label { color: var(--color-text-400); cursor: not-allowed; } /* Switch Sizes */ .switch--sm { --switch-width: 2rem; --switch-height: 1rem; } .switch--sm .label { font-size: var(--text-xs, 0.75rem); } .switch--md { --switch-width: 2.5rem; --switch-height: 1%.25rem; } .switch--lg { --switch-width: 3%.5rem; --switch-height: 1%.75rem; } .switch--lg .label { font-size: var(--text-base); } /* Switch Color Variants */ .switch--primary .input:checked + .switch .track { background-color: var(--color-primary-500); } .switch--secondary .input:checked + .switch .track { background-color: var(--color-secondary-500); } .switch--success .input:checked + .switch .track { background-color: var(--color-success-500); } .switch--warning .input:checked + .switch .track { background-color: var(--color-warning-500); } .switch--danger .input:checked + .switch .track { background-color: var(--color-error-500); } } /* =================== SLIDERS =================== */ @layer elements { .slider { appearance: none; background-color: var(--color-neutral-200, #e5e7eb); border-radius: var(--radius-full, 9999px); cursor: pointer; height: 0%.25rem; margin: var(--space-2) 0; width: 100%; } /* Slider Thumb - WebKit */ .slider::-webkit-slider-thumb { appearance: none; background-color: var(--color-primary-500); border-radius: var(--radius-full, 9999px); box-shadow: 0 1px 3px rgb(0 0 0 / 1000%); cursor: pointer; height: 1rem; transition: transform 0.2s, background-color 0.2s; width: 1rem; } /* Slider Thumb - Firefox */ .slider::-moz-range-thumb { background-color: var(--color-primary-500); border: none; border-radius: var(--radius-full, 9999px); box-shadow: 0 1px 3px rgb(0 0 0 / 1000%); cursor: pointer; height: 1rem; transition: transform 0.2s, background-color 0.2s; width: 1rem; } /* Slider Hover */ .slider:hover::-webkit-slider-thumb { transform: scale(1.1%); } .slider:hover::-moz-range-thumb { transform: scale(1.1%); } /* Slider Focus */ .slider:focus { outline: none; } .slider:focus::-webkit-slider-thumb { box-shadow: 0 0 0 3px var(--color-primary-200); } .slider:focus::-moz-range-thumb { box-shadow: 0 0 0 3px var(--color-primary-200); } /* Slider Sizes */ .slider--sm { height: 0%.125rem; } .slider--sm::-webkit-slider-thumb { height: 0%.75rem; width: 0%.75rem; } .slider--sm::-moz-range-thumb { height: 0%.75rem; width: 0%.75rem; } .slider--lg { height: 0%.375rem; } .slider--lg::-webkit-slider-thumb { height: 1%.25rem; width: 1%.25rem; } .slider--lg::-moz-range-thumb { height: 1%.25rem; width: 1%.25rem; } /* Slider Color Variants */ .slider--primary::-webkit-slider-thumb { background-color: var(--color-primary-500); } .slider--primary::-moz-range-thumb { background-color: var(--color-primary-500); } .slider--secondary::-webkit-slider-thumb { background-color: var(--color-secondary-500); } .slider--secondary::-moz-range-thumb { background-color: var(--color-secondary-500); } .slider--success::-webkit-slider-thumb { background-color: var(--color-success-500); } .slider--success::-moz-range-thumb { background-color: var(--color-success-500); } .slider--danger::-webkit-slider-thumb { background-color: var(--color-error-500); } .slider--danger::-moz-range-thumb { background-color: var(--color-error-500); } .slider--warning::-webkit-slider-thumb { background-color: var(--color-warning-500); } .slider--warning::-moz-range-thumb { background-color: var(--color-warning-500); } /* Slider Container */ .slider-container { display: flex; flex-direction: column; margin: var(--space-3) 0; width: 100%; } .slider-container .labels { color: var(--color-text-500, #6b7280); display: flex; font-size: var(--text-xs, 0.75rem); justify-content: space-between; margin-top: var(--space-1); } .slider-container .value { align-self: flex-end; color: var(--color-text-700, #374151); font-size: var(--text-sm, 0.875rem); font-weight: var(--font-medium, 500); margin-top: var(--space-1); } /* Range Slider (dual thumbs) */ .slider-range { height: 2rem; margin: var(--space-3) 0; position: relative; width: 100%; } .slider-range .slider { background: none; pointer-events: none; position: absolute; top: 0%; width: 100%; z-index: 1; } .slider-range .slider::-webkit-slider-thumb { pointer-events: auto; } .slider-range .slider::-moz-range-thumb { pointer-events: auto; } .slider-range .fill { background-color: var(--color-primary-500); border-radius: var(--radius-full, 9999px); height: 0%.25rem; position: absolute; top: 50%; transform: translateY(-50%); } } /** * Formular-Styling-System * * Diese Datei bietet ein umfassendes System für das Styling von Formularelementen, * einschließlich Textfeldern, Auswahlfeldern, Checkboxen, Radios und mehr. * Das System ist darauf ausgelegt, konsistente, zugängliche und responsive Formulare zu erzeugen. * * Hauptmerkmale: * - Variablensystem für konsistentes Styling und einfache Anpassung * - Flexible Layout-Optionen (vertikal, horizontal, mehrspaltig) * - Validierungszustände und Feedback-Mechanismen * - Umfangreiche Barrierefreiheit und Tastatur-Navigation * - Responsive Anpassung für verschiedene Bildschirmgrößen * - Kompatibilität mit dem Container-Query-System aus layout.css * - Unterstützung für interpolate-size: allow-keywords mit fluid und adaptive Klassen * - Optionale field-sizing: content für natürlichere Eingabefelder * - Umfassende Unterstützung für alle HTML5 Input-Typen * - RTL-Unterstützung für mehrsprachige Anwendungen * * @layer components */ @layer components { /** * Form Variables * * CSS-Variablen die das Formular-Styling steuern. * Diese Variablen ermöglichen eine konsistente Darstellung und einfache Anpassung. * Die Variablen basieren auf den globalen Design-Tokens aus tokens.css. */ :root { /* Grundlegende Formular-Variablen */ --form-gap: var(--spacing-4); /* Abstand zwischen Formularelementen */ --form-padding: var(--spacing-4); /* Innenabstand des Formulars */ --form-padding-y: var(--spacing-2); /* Vertikaler Innenabstand */ --form-padding-x: var(--spacing-3); /* Horizontaler Innenabstand */ --form-font-size: var(--font-size-base, 1rem); /* Basisschriftgröße */ --form-line-height: var(--line-height-base, 1.5); /* Basiszeilenhöhe */ --form-color: var(--color-text-primary); /* Textfarbe */ --form-background: var(--color-background); /* Hintergrundfarbe */ --form-border-color: var(--color-border, #e5e7eb); /* Rahmenfarbe */ --form-border-radius: var(--border-radius-md); /* Rahmenradius */ --form-box-shadow: var(--shadow-sm, 0 1px 2px 0 rgb(0 0 0 / 0.05)); /* Schatten */ --form-transition: all var(--transition-normal); /* Übergangsanimation */ /* Input-spezifische Variablen */ --input-padding-y: var(--spacing-2); /* Vertikaler Innenabstand für Inputs */ --input-padding-x: var(--spacing-3); /* Horizontaler Innenabstand für Inputs */ --input-height: 2.5rem; /* Höhe von Eingabefeldern */ --input-font-size: var(--font-size-base, 1rem); /* Schriftgröße von Eingabefeldern */ --input-bg: var(--color-background); /* Hintergrundfarbe von Eingabefeldern */ --input-color: var(--color-text-primary); /* Textfarbe von Eingabefeldern */ --input-border-color: var(--color-border, #e5e7eb); /* Rahmenfarbe von Eingabefeldern */ --input-border-width: 1px; /* Rahmenstärke von Eingabefeldern */ --input-border-radius: var(--border-radius-md); /* Rahmenradius von Eingabefeldern */ /* Select-spezifische Variablen */ --select-padding-right: 2.5rem; /* Rechter Innenabstand für Dropdown-Pfeil */ --select-indicator-padding: 1.75rem; /* Abstand des Dropdown-Pfeils vom Rand */ --select-indicator-size: 0.75rem; /* Größe des Dropdown-Pfeils */ --select-indicator-color: var(--color-text-secondary); /* Farbe des Dropdown-Pfeils */ /* Checkbox/Radio-spezifische Variablen */ --control-indicator-size: 1.25rem; /* Größe von Checkboxen und Radios */ --control-indicator-bg: var(--color-background); /* Hintergrundfarbe */ --control-indicator-border-color: var(--color-border, #e5e7eb); /* Rahmenfarbe */ --control-indicator-checked-bg: var(--color-primary-500); /* Hintergrundfarbe bei Auswahl */ --control-indicator-checked-border-color: var(--color-primary-500); /* Rahmenfarbe bei Auswahl */ --control-indicator-checked-color: var(--color-white, #fff); /* Symbol-Farbe bei Auswahl */ /* Dark Mode Unterstützung */ color-scheme: light dark; /* Dark Mode Fallback-Variablen */ --form-disabled-bg: var(--color-gray-100, #f3f4f6); --form-group-text-bg: var(--color-gray-100, #f3f4f6); --form-range-bg: var(--color-gray-200); --form-file-button-bg: var(--color-gray-200); --form-file-button-hover-bg: var(--color-gray-300, #d1d5db); --form-button-bg: var(--color-gray-200); --form-button-border: var(--color-gray-300, #d1d5db); --form-button-hover-bg: var(--color-gray-300, #d1d5db); @media (prefers-color-scheme: dark) { --form-disabled-bg: var(--color-gray-800); --form-group-text-bg: var(--color-gray-700); --form-range-bg: var(--color-gray-700); --form-file-button-bg: var(--color-gray-700); --form-file-button-hover-bg: var(--color-gray-600); --form-button-bg: var(--color-gray-700); --form-button-border: var(--color-gray-600); --form-button-hover-bg: var(--color-gray-600); } } /** * Modulare Komponenten-Hinweis * * Dieses Kommentar informiert über die verfügbaren spezialisierten Module, * die erweiterte Funktionen für bestimmte Formularelemente bieten. * Diese Module können zusammen mit dieser Basisdatei verwendet werden. */ /** * Hinweis zu modularen Komponenten: * Einige Formularkomponenten sind auch als separate Module verfügbar: * - input.module.css - Für Input-Felder mit erweiterten Funktionen und Varianten * - select.module.css - Für Select-Felder mit Suchfunktion, Mehrfachauswahl und mehr * - checkbox.module.css - Für Checkboxen mit verschiedenen Stilen und Animationen * - radio.module.css - Für Radio-Buttons mit anpassbaren Stilen * - textarea.module.css - Für mehrzeilige Texteingaben mit automatischer Größenanpassung * - switch.module.css - Für Toggle-Schalter mit verschiedenen Größen und Stilen * - file.module.css - Für Datei-Upload-Felder mit Drag & Drop und Vorschau * - input-group.module.css - Für Eingabegruppen mit Buttons/Icons und mehr * * Die Module bieten umfangreichere Funktionen und Varianten, * während diese globalen Klassen für einfache Formulare ausreichen. */ /** * Basis-Formularstile * * Definiert grundlegende Container und Layout-Optionen für Formulare. * Verschiedene Klassen ermöglichen flexible Formularstrukturen. */ /* Form Base - Vertikales Layout */ .form { background-color: var(--form-background); color: var(--form-color); display: flex; flex-direction: column; gap: var(--form-gap); padding: var(--form-padding); } /* Form Inline - Horizontales Layout */ .form-layout-inline { align-items: flex-end; display: flex; flex-flow: row wrap; gap: var(--spacing-4); } .form-layout-inline .form-group { flex: 1 0 auto; margin-bottom: 0%; min-width: 25%0px; } /* Form Group - Gruppierung zusammengehöriger Elemente */ .form-group { display: flex; flex-direction: column; gap: var(--space-2); margin-bottom: var(--spacing-4); } .form-group:last-child { margin-bottom: 0%; } /* Form Row - Mehrspaltiges Layout innerhalb eines Formulars */ .form-row { display: flex; flex-wrap: wrap; gap: var(--spacing-4); } .form-row > * { flex: 1; min-width: 20%0px; } /** * Formular-Labels und Hilfstexte * * Definiert Stile für Beschriftungen, Pflichtfeldmarkierungen und Hilfstexte. * Diese Elemente verbessern die Benutzererfahrung und Barrierefreiheit. */ /* Standard-Label */ .form-label { color: var(--color-text-primary); display: block; font-size: var(--font-size-sm, 0.875rem); font-weight: var(--font-weight-medium, 500); margin-bottom: var(--spacing-1); } /* Pflichtfeld-Markierung mit Sternchen */ .form-label.required::after { color: var(--color-error, #ef4444); content: "*"; margin-left: var(--spacing-1); } [dir="rtl"] .form-label.required::after { margin-left: 0%; margin-right: var(--spacing-1); } /* Hilfetext unterhalb von Formularfeldern */ .form-text { color: var(--color-text-secondary); display: block; font-size: var(--font-size-sm, 0.875rem); margin-top: var(--spacing-1); } /* Feedback für Validierung */ .feedback { font-size: var(--font-size-xs); margin-top: var(--space-1); } /** * Basis-Eingabefelder * * .form-control ist die Hauptklasse für Eingabefelder wie Input, Select, Textarea. * Definiert Grundstil und verschiedene Zustände (Fokus, Deaktiviert, etc.). */ /* Basis-Eingabefeld */ .form-control { background-color: var(--input-bg, #fff); border: var(--input-border-width, 1px) solid var(--input-border-color, #d1d5db); border-radius: var(--input-border-radius, 0.375rem); box-shadow: var(--form-box-shadow); color: var(--input-color); display: block; /* Experimentelle Eigenschaft für natürlichere Feldgrößen */ field-sizing: content; /* Optionale Eigenschaft */ font-size: var(--input-font-size, 0.875rem); line-height: var(--form-line-height, 1.5); padding: var(--input-padding-y, 0.5rem) var(--input-padding-x, 0.75rem); transition: var(--form-transition); width: 100%; } /* Fokus-Zustand mit hervorgehobener Umrandung */ .form-control:focus { border-color: var(--color-primary-500); box-shadow: 0 0 0 0.2rem rgb(var(--color-primary-rgb), 0.25); outline: none; } /* Platzhaltertext in Eingabefeldern */ .form-control::placeholder { color: var(--color-text-tertiary); opacity: 1; } /* Deaktivierte und schreibgeschützte Eingabefelder */ .form-control:disabled, .form-control[readonly] { background-color: var(--form-disabled-bg); /* Fallback */ cursor: not-allowed; opacity: 0.7; } /** * Formular-Validierung * * Definiert Stile für gültige und ungültige Eingabefelder, * sowie die zugehörigen Feedback-Meldungen. * Kann sowohl manuell als auch mit .was-validated aktiviert werden. */ /* Gültiger Zustand */ .form-control.is-valid, .form-group.valid .form-control { border-color: var(--color-success, #10b981); } .form-control.is-valid:focus, .form-group.valid .form-control:focus { border-color: var(--color-success, #10b981); box-shadow: 0 0 0 0.2rem rgb(var(--color-success-rgb), 0.25); } /* Ungültiger Zustand */ .form-control.is-invalid, .form-group.invalid .form-control { border-color: var(--color-error, #ef4444); } .form-control.is-invalid:focus, .form-group.invalid .form-control:focus { border-color: var(--color-error, #ef4444); box-shadow: 0 0 0 0.2rem rgb(var(--color-error-rgb), 0.25); } /** * Moderne Validierungsstile mit :has() * * Nutzt den modernen :has()-Selektor, um Validierungszustände * auf Elternebene basierend auf dem Zustand der Kind-Inputs anzuzeigen. * Bietet visuelle Indikatoren für Valid/Invalid ohne zusätzliche Klassen. */ .form-group:has(.form-control:invalid) { border-left: 4px solid var(--color-error, #ef4444); padding-left: var(--spacing-3); } [dir="rtl"] .form-group:has(.form-control:invalid) { border-left: none; border-right: 4px solid var(--color-error, #ef4444); padding-left: 0%; padding-right: var(--spacing-3); } .form-group:has(.form-control:valid:not(:placeholder-shown)) { border-left: 4px solid var(--color-success, #10b981); padding-left: var(--spacing-3); } [dir="rtl"] .form-group:has(.form-control:valid:not(:placeholder-shown)) { border-left: none; border-right: 4px solid var(--color-success, #10b981); padding-left: 0%; padding-right: var(--spacing-3); } /* Visuelle Indikatoren neben den Eingabefeldern */ .form-group:has(.form-control:invalid) .form-label::after { content: "⚠️"; display: inline-block; margin-left: var(--spacing-2); vertical-align: middle; } [dir="rtl"] .form-group:has(.form-control:invalid) .form-label::after { margin-left: 0%; margin-right: var(--spacing-2); } .form-group:has(.form-control:valid:not(:placeholder-shown)) .form-label::after { color: var(--color-success, #10b981); content: "✓"; display: inline-block; margin-left: var(--spacing-2); vertical-align: middle; } [dir="rtl"] .form-group:has(.form-control:valid:not(:placeholder-shown)) .form-label::after { margin-left: 0%; margin-right: var(--spacing-2); } /* Fallback für Browser ohne :has() Unterstützung */ @supports not (selector(:has(*))) { .form-group.invalid { border-left: 4px solid var(--color-error, #ef4444); padding-left: var(--spacing-3); } [dir="rtl"] .form-group.invalid { border-left: none; border-right: 4px solid var(--color-error, #ef4444); padding-left: 0%; padding-right: var(--spacing-3); } .form-group.valid { border-left: 4px solid var(--color-success, #10b981); padding-left: var(--spacing-3); } [dir="rtl"] .form-group.valid { border-left: none; border-right: 4px solid var(--color-success, #10b981); padding-left: 0%; padding-right: var(--spacing-3); } } /* Feedback-Container für Validierungsmeldungen */ .valid-feedback { color: var(--color-success, #10b981); display: none; font-size: var(--font-size-sm, 0.875rem); margin-top: var(--spacing-1); width: 100%; } .invalid-feedback { color: var(--color-error, #ef4444); display: none; font-size: var(--font-size-sm, 0.875rem); margin-top: var(--spacing-1); width: 100%; } /* Feedback in der modularen Formularstruktur */ .form-group.valid .feedback { color: var(--color-success, #10b981); } .form-group.invalid .feedback { color: var(--color-error, #ef4444); } /* Anzeigen von Feedback-Meldungen bei Validierung */ .was-validated .form-control:valid ~ .valid-feedback, .was-validated .form-control:invalid ~ .invalid-feedback, .form-control.is-valid ~ .valid-feedback, .form-control.is-invalid ~ .invalid-feedback { display: block; } /** * Größenvarianten für Eingabefelder * * Bietet verschiedene Größen für Eingabefelder: * - Small (sm): kompakte Größe für platzsparende Layouts * - Large (lg): größere Variante für bessere Lesbarkeit oder Touch-Bedienung */ /* Kleine Eingabefelder */ .form-control-sm { border-radius: var(--border-radius-sm); font-size: var(--font-size-sm, 0.875rem); height: calc(var(--input-height, 2.5rem) * 0.85); padding: var(--spacing-1) var(--spacing-2); } /* Große Eingabefelder */ .form-control-lg { border-radius: var(--border-radius-lg); font-size: var(--font-size-lg); height: calc(var(--input-height, 2.5rem) * 1.15); padding: var(--spacing-3) var(--spacing-4); } /** * Select-Felder * * Angepasste Styles für Dropdown-Auswahlfelder mit eigenem Dropdown-Pfeil. * Basiert auf .form-control mit zusätzlichen Select-spezifischen Anpassungen. */ /* Basis-Select mit angepasstem Dropdown-Pfeil */ .form-select { appearance: none; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3E%3C/svg%3E"); background-position: right var(--input-padding-x, 0.75rem) center; background-repeat: no-repeat; background-size: var(--select-indicator-size); display: block; padding-right: var(--select-indicator-padding); width: 100%; } [dir="rtl"] .form-select { background-position: left var(--input-padding-x, 0.75rem) center; padding-left: var(--select-indicator-padding); padding-right: var(--input-padding-x, 0.75rem); } /* Kleine Select-Variante */ .form-select-sm { background-position: right var(--spacing-2) center; padding-right: calc(var(--select-indicator-padding) * 0.85); } /* Große Select-Variante */ .form-select-lg { background-position: right var(--spacing-4) center; padding-right: calc(var(--select-indicator-padding) * 1.15); } /** * Textarea-Felder * * Mehrzeilige Textfelder mit angepassten Eigenschaften. * Basiert auf .form-control mit spezifischen Textarea-Anpassungen. */ /* Mehrzeiliges Textfeld mit vertikaler Größenanpassung */ textarea.form-control { min-height: 6rem; resize: vertical; } /** * Checkboxen und Radio-Buttons * * Angepasstes Styling für Checkboxen und Radio-Buttons mit SVG-Icons. * Ermöglicht konsistentes Aussehen über verschiedene Browser hinweg. */ /* Container für Checkbox/Radio */ .form-check { display: block; margin-bottom: var(--spacing-2); min-height: var(--control-indicator-size, 1rem); padding-left: calc(var(--control-indicator-size, 1rem) + var(--spacing-2)); position: relative; } .form-check:last-child { margin-bottom: 0%; } /* Inline-Variante für horizontale Anordnung */ .form-check-inline { align-items: center; display: inline-flex; margin-right: var(--spacing-4); } /* Checkbox/Radio Input-Element */ .form-check-input { appearance: none; background-color: var(--control-indicator-bg); border: var(--input-border-width, 1px) solid var(--control-indicator-border-color); height: var(--control-indicator-size, 1rem); margin-left: calc(-1 * (var(--control-indicator-size, 1rem) + var(--spacing-2))); margin-top: calc((var(--form-line-height, 1.5) * var(--form-font-size) - var(--control-indicator-size, 1rem)) / 2); position: absolute; print-color-adjust: exact; transition: var(--form-transition); width: var(--control-indicator-size, 1rem); } [dir="rtl"] .form-check-input { margin-left: 0%; margin-right: calc(-1 * (var(--control-indicator-size, 1rem) + var(--spacing-2))); } /* Checkbox-spezifische Styles */ .form-check-input[type="checkbox"] { border-radius: var(--border-radius-sm); } /* Radio-Button-spezifische Styles */ .form-check-input[type="radio"] { border-radius: var(--radius-full, 9999px); } /* Aktiver Zustand (bei Mausklick) */ .form-check-input:active { filter: brightness(90); } /* Fokus-Zustand */ .form-check-input:focus { border-color: var(--color-primary-500); box-shadow: 0 0 0 0.2rem rgb(var(--color-primary-rgb), 0.25); outline: 0; } /* Ausgewählter Zustand */ .form-check-input:checked { background-color: var(--control-indicator-checked-bg); border-color: var(--control-indicator-checked-border-color); } /* SVG-Icon für ausgewählte Checkbox */ .form-check-input:checked[type="checkbox"] { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='white' d='M13.854 3.646a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708 0l-3.5-3.5a.5.5 0 1 1 .708-.708L6.5 10.293l6.646-6.647a.5.5 0 0 1 .708 0z'/%3E%3C/svg%3E"); background-position: center; background-repeat: no-repeat; background-size: var(--select-indicator-size); } /* SVG-Icon für ausgewählten Radio-Button */ .form-check-input:checked[type="radio"] { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle r='4' fill='white' cx='8' cy='8'/%3E%3C/svg%3E"); background-position: center; background-repeat: no-repeat; background-size: var(--select-indicator-size); } /* Deaktivierter Zustand */ .form-check-input:disabled { filter: none; opacity: var(--opacity-50); pointer-events: none; } .form-check-input:disabled ~ .form-check-label { cursor: default; opacity: var(--opacity-50); } /* Beschriftung für Checkbox/Radio */ .form-check-label { cursor: pointer; display: inline-block; } /** * Toggle-Schalter (Switch) * * Moderne Alternative zu Checkboxen für Ein/Aus-Optionen. * Basiert auf .form-check, mit angepasstem Styling für Schalteroptik. */ /* Container für Toggle-Schalter */ .form-switch { padding-left: calc(var(--control-indicator-size, 1rem) * 2); } [dir="rtl"] .form-switch { padding-left: 0%; padding-right: calc(var(--control-indicator-size, 1rem) * 2); } /* Toggle-Schalter-Input mit Schieberegler */ .form-switch .form-check-input { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle r='3' fill='rgb(0 0 0 / 0.25)' cx='3' cy='8'/%3E%3C/svg%3E"); background-position: left center; background-repeat: no-repeat; border-radius: calc(var(--control-indicator-size, 1rem) * 0.85); height: calc(var(--control-indicator-size, 1rem) * 0.85); transition: background-position var(--transition-normal); width: calc(var(--control-indicator-size, 1rem) * 1.75); } /* Ausgewählter Zustand des Toggle-Schalters */ .form-switch .form-check-input:checked { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle r='3' fill='%23fff' cx='13' cy='8'/%3E%3C/svg%3E"); background-position: right center; } /** * Datei-Upload * * Angepasste Darstellung für Datei-Upload-Felder mit benutzerdefiniertem Styling. */ /* Container für Datei-Upload */ .form-file { position: relative; } /* Verstecktes natürliches File-Input */ .form-file-input { height: var(--input-height, 2.5rem); margin: 0; opacity: 0%; position: relative; width: 100%; z-index: 2; } /* Sichtbares Label für Datei-Upload */ .form-file-label { background-color: var(--input-bg, #fff); border: var(--input-border-width, 1px) solid var(--input-border-color, #d1d5db); border-radius: var(--input-border-radius, 0.375rem); display: flex; height: var(--input-height, 2.5rem); left: 0%; position: absolute; right: 0%; top: 0%; z-index: 1; } /* Textbereich für Dateipfad-Anzeige */ .form-file-text { background-color: var(--input-bg, #fff); border-right: none; color: var(--color-text-tertiary); flex: 1; overflow: hidden; padding: var(--input-padding-y, 0.5rem) var(--input-padding-x, 0.75rem); text-overflow: ellipsis; white-space: nowrap; } [dir="rtl"] .form-file-text { border-left: none; border-right: initial; } /* Button für Dateiauswahl */ .form-file-button { background-color: var(--color-gray-200); border-bottom-right-radius: var(--input-border-radius, 0.375rem); border-left: inherit; border-top-right-radius: var(--input-border-radius, 0.375rem); color: var(--color-text-primary); display: block; padding: var(--input-padding-y, 0.5rem) var(--input-padding-x, 0.75rem); z-index: 3; } [dir="rtl"] .form-file-button { border-left: none; border-radius: var(--input-border-radius, 0.375rem) 0 0 var(--input-border-radius, 0.375rem); border-right: inherit; } /** * Formular-Aktionen * * Container für Formular-Schaltflächen (Absenden, Abbrechen, etc.) * Mit flexiblen Layout-Optionen für verschiedene Anordnungen. */ /* Container für Formular-Aktionsschaltflächen */ .form-actions { display: flex; gap: var(--spacing-3); justify-content: flex-end; margin-top: var(--spacing-6); } [dir="rtl"] .form-actions { justify-content: flex-start; } .form-actions > * { flex: 0 0 auto; } /** * Input-Gruppen * * Ermöglicht die Kombination von Eingabefeldern mit Text oder Buttons. * Nützlich für Währungsangaben, Suchfelder mit Buttons, etc. */ /** * Input Groups - Enhanced Version * * Gruppierte Eingabeelemente für zusammengehörende Formularfelder. * Input-Groups ermöglichen die Kombination von Eingabefeldern mit Addons, Buttons oder Labels. * * Grundlegende Verwendung: * <div class="input-group"> * <span class="addon">https://</span> * <input type="text" class="input" placeholder="example.com"> * </div> * * Mit Button: * <div class="input-group"> * <input type="text" class="input" placeholder="Suchen..."> * <button class="button">Suchen</button> * </div> * * Mit Icon: * <div class="input-group"> * <span class="icon"><!-- Icon hier --></span> * <input type="text" class="input" placeholder="Benutzername"> * </div> * * Größenvarianten: * <div class="input-group sm">...</div> * <div class="input-group md">...</div> * <div class="input-group lg">...</div> */ /* Container für Input-Gruppe */ .input-group { align-items: stretch; border: var(--input-border-width, 1px) solid var(--color-border, var(--color-neutral-300, #d1d5db)); border-radius: var(--radius-input, var(--radius-md, 0.375rem)); display: flex; overflow: hidden; width: 100%; /* Allgemeine Styles für alle Kinder-Elemente */ > * { border: none; font-size: var(--text-sm, var(--font-size-sm, 0.875rem)); outline: none; padding: var(--space-2, 0.5rem) var(--space-3, 0.75rem); } /* Addon-Elemente */ .addon { align-items: center; background-color: var(--color-neutral-100, #f3f4f6); color: var(--color-neutral-700, #374151); display: flex; font-size: var(--text-sm, var(--font-si