Premium Vue Command/Search Input mit animiertem Gradient-Ring, Floating Label, Live-Suggestions, Keyboard-Navigation, Clear-Button, Ladezustand, Statuszeile und sauberem v-model-Support. Funktioniert auch direkt in der Komponenten-Vorschau ohne externe Abhängigkeiten.
1<template>2 <div3 class="nebula-command"4 :class="{5 'is-open': open && visibleSuggestions.length > 0,6 'is-focused': focused,7 'has-value': localValue.length > 0,8 'is-loading': loading,9 'is-disabled': disabled,10 'just-searched': searched11 }"12 >13 <div class="nebula-frame">14 <div class="nebula-orb orb-one" aria-hidden="true"></div>15 <div class="nebula-orb orb-two" aria-hidden="true"></div>16 <div class="nebula-orb orb-three" aria-hidden="true"></div>17
18 <label19 class="nebula-shell"20 :for="inputId"21 role="combobox"22 :aria-expanded="open && visibleSuggestions.length > 0 ? 'true' : 'false'"23 :aria-owns="listId"24 :aria-haspopup="'listbox'"25 >26 <span class="nebula-search-icon" aria-hidden="true">27 <svg viewBox="0 0 24 24">28 <path d="M10.75 18.25a7.5 7.5 0 1 1 0-15 7.5 7.5 0 0 1 0 15Z" />29 <path d="M16.25 16.25 21 21" />30 </svg>31 </span>32
33 <span class="nebula-label">{{ label }}</span>34
35 <input36 :id="inputId"37 ref="input"38 class="nebula-input"39 :value="localValue"40 :placeholder="focused ? placeholder : ' '"41 :disabled="disabled"42 :aria-controls="listId"43 :aria-activedescendant="activeOptionId"44 autocomplete="off"45 spellcheck="false"46 @input="onInput"47 @focus="onFocus"48 @blur="onBlur"49 @keydown="onKeydown"50 />51
52 <span v-if="shortcut" class="nebula-shortcut" aria-hidden="true">53 {{ shortcut }}54 </span>55
56 <button57 v-if="localValue.length > 0 && !disabled && !loading"58 class="nebula-action"59 type="button"60 aria-label="Eingabe löschen"61 @mousedown.prevent62 @click="clearInput"63 >64 <svg viewBox="0 0 24 24">65 <path d="M7 7l10 10M17 7 7 17" />66 </svg>67 </button>68
69 <span v-if="loading" class="nebula-loader" aria-label="Lädt"></span>70 </label>71
72 <div73 v-if="open && visibleSuggestions.length > 0"74 :id="listId"75 class="nebula-list"76 role="listbox"77 >78 <button79 v-for="(item, index) in visibleSuggestions"80 :id="optionId(index)"81 :key="item"82 class="nebula-option"83 :class="{ 'is-active': selectedIndex === index }"84 type="button"85 role="option"86 :aria-selected="selectedIndex === index ? 'true' : 'false'"87 @mousedown.prevent88 @click="chooseSuggestion(item)"89 >90 <span class="nebula-option-icon" aria-hidden="true">91 <svg viewBox="0 0 24 24">92 <path d="M5 12h12" />93 <path d="m13 8 4 4-4 4" />94 </svg>95 </span>96
97 <span class="nebula-option-text">98 <strong>{{ item }}</strong>99 <small>{{ suggestionSubtitle(item) }}</small>100 </span>101 </button>102 </div>103 </div>104
105 <p class="nebula-status" aria-live="polite">106 <span class="nebula-status-dot" aria-hidden="true"></span>107 {{ statusText }}108 </p>109 </div>110</template>111
112<script>113export default {114 name: 'NebulaCommandInput',115
116 props: {117 modelValue: {118 type: String,119 default: ''120 },121
122 label: {123 type: String,124 default: 'Suche starten'125 },126
127 placeholder: {128 type: String,129 default: 'Komponente, Kategorie oder Tag suchen...'130 },131
132 hint: {133 type: String,134 default: 'Tippe etwas ein oder wähle einen Vorschlag aus.'135 },136
137 shortcut: {138 type: String,139 default: '⌘ K'140 },141
142 loading: {143 type: Boolean,144 default: false145 },146
147 disabled: {148 type: Boolean,149 default: false150 },151
152 suggestions: {153 type: Array,154 default: function () {155 return [156 'Aurora Button',157 'Glass Card',158 'Neon Loader',159 'Gradient Input',160 'Pricing Table',161 'Command Menu',162 'Floating Label',163 'Dark Dashboard'164 ]165 }166 }167 },168
169 emits: ['update:modelValue', 'search', 'clear', 'select'],170
171 data: function () {172 return {173 localValue: this.modelValue || '',174 focused: false,175 open: false,176 selectedIndex: -1,177 searched: false,178 inputId: 'nebula-input-' + Math.random().toString(36).slice(2, 10),179 listId: 'nebula-list-' + Math.random().toString(36).slice(2, 10)180 }181 },182
183 computed: {184 visibleSuggestions: function () {185 var value = String(this.localValue || '').toLowerCase().trim()186
187 if (!value) {188 return this.suggestions.slice(0, 4)189 }190
191 return this.suggestions192 .filter(function (item) {193 return String(item).toLowerCase().indexOf(value) !== -1194 })195 .slice(0, 5)196 },197
198 activeOptionId: function () {199 if (this.selectedIndex < 0) {200 return null201 }202
203 return this.optionId(this.selectedIndex)204 },205
206 statusText: function () {207 if (this.disabled) {208 return 'Dieses Eingabefeld ist aktuell deaktiviert.'209 }210
211 if (this.loading) {212 return 'Suche läuft gerade...'213 }214
215 if (this.localValue.length > 0 && this.visibleSuggestions.length === 0) {216 return 'Keine passenden Vorschläge gefunden.'217 }218
219 if (this.localValue.length > 0 && this.visibleSuggestions.length === 1) {220 return 'Ein passender Vorschlag gefunden.'221 }222
223 if (this.localValue.length > 0 && this.visibleSuggestions.length > 1) {224 return this.visibleSuggestions.length + ' passende Vorschläge gefunden.'225 }226
227 return this.hint228 }229 },230
231 watch: {232 modelValue: function (newValue) {233 this.localValue = newValue || ''234 }235 },236
237 methods: {238 onInput: function (event) {239 this.localValue = event.target.value240 this.open = true241 this.selectedIndex = this.visibleSuggestions.length > 0 ? 0 : -1242
243 this.$emit('update:modelValue', this.localValue)244 },245
246 onFocus: function () {247 if (this.disabled) {248 return249 }250
251 this.focused = true252 this.open = true253
254 if (this.visibleSuggestions.length > 0 && this.selectedIndex < 0) {255 this.selectedIndex = 0256 }257 },258
259 onBlur: function () {260 var self = this261
262 window.setTimeout(function () {263 self.focused = false264 self.open = false265 self.selectedIndex = -1266 }, 120)267 },268
269 onKeydown: function (event) {270 if (event.key === 'ArrowDown') {271 event.preventDefault()272 this.moveSelection(1)273 }274
275 if (event.key === 'ArrowUp') {276 event.preventDefault()277 this.moveSelection(-1)278 }279
280 if (event.key === 'Enter') {281 event.preventDefault()282 this.submitSelection()283 }284
285 if (event.key === 'Escape') {286 this.open = false287 this.selectedIndex = -1288 }289 },290
291 moveSelection: function (direction) {292 var max = this.visibleSuggestions.length - 1293
294 if (max < 0) {295 this.selectedIndex = -1296 return297 }298
299 if (this.selectedIndex < 0) {300 this.selectedIndex = 0301 return302 }303
304 this.selectedIndex = this.selectedIndex + direction305
306 if (this.selectedIndex > max) {307 this.selectedIndex = 0308 }309
310 if (this.selectedIndex < 0) {311 this.selectedIndex = max312 }313 },314
315 submitSelection: function () {316 if (this.open && this.selectedIndex >= 0 && this.visibleSuggestions[this.selectedIndex]) {317 this.chooseSuggestion(this.visibleSuggestions[this.selectedIndex])318 return319 }320
321 this.triggerSearch()322 },323
324 chooseSuggestion: function (item) {325 this.localValue = item326 this.open = false327 this.selectedIndex = -1328 this.searched = true329
330 this.$emit('update:modelValue', item)331 this.$emit('select', item)332 this.$emit('search', item)333
334 this.resetSearchPulse()335 },336
337 clearInput: function () {338 this.localValue = ''339 this.open = true340 this.selectedIndex = 0341
342 this.$emit('update:modelValue', '')343 this.$emit('clear')344
345 if (this.$refs.input) {346 this.$refs.input.focus()347 }348 },349
350 triggerSearch: function () {351 this.searched = true352 this.$emit('search', this.localValue)353 this.resetSearchPulse()354 },355
356 resetSearchPulse: function () {357 var self = this358
359 window.setTimeout(function () {360 self.searched = false361 }, 520)362 },363
364 optionId: function (index) {365 return this.listId + '-option-' + index366 },367
368 suggestionSubtitle: function (item) {369 var text = String(item).toLowerCase()370
371 if (text.indexOf('button') !== -1) {372 return 'Interaktive Button-Komponente'373 }374
375 if (text.indexOf('card') !== -1) {376 return 'Layout- und Content-Komponente'377 }378
379 if (text.indexOf('loader') !== -1) {380 return 'Animierte Lade-Komponente'381 }382
383 if (text.indexOf('input') !== -1 || text.indexOf('label') !== -1) {384 return 'Formular- und Eingabe-Komponente'385 }386
387 if (text.indexOf('dashboard') !== -1) {388 return 'UI-Pattern für Admin-Oberflächen'389 }390
391 return 'Passender Elementa-Vorschlag'392 }393 }394}395</script>396
397<style scoped>398.nebula-command {399 --nebula-bg: #080b14;400 --nebula-panel: rgba(13, 18, 31, 0.94);401 --nebula-panel-strong: rgba(16, 23, 42, 0.98);402 --nebula-line: rgba(255, 255, 255, 0.13);403 --nebula-line-strong: rgba(255, 255, 255, 0.22);404 --nebula-text: #f8fafc;405 --nebula-muted: #94a3b8;406 --nebula-soft: rgba(148, 163, 184, 0.13);407 --nebula-pink: #fb7185;408 --nebula-orange: #f97316;409 --nebula-lime: #a3e635;410 --nebula-cyan: #22d3ee;411 --nebula-violet: #a78bfa;412
413 width: min(100%, 480px);414 color: var(--nebula-text);415 font-family:416 Inter,417 ui-sans-serif,418 system-ui,419 -apple-system,420 BlinkMacSystemFont,421 "Segoe UI",422 sans-serif;423}424
425.nebula-frame {426 position: relative;427 border-radius: 28px;428 isolation: isolate;429}430
431.nebula-frame::before {432 content: "";433 position: absolute;434 inset: -1px;435 z-index: -4;436 border-radius: inherit;437 background:438 linear-gradient(439 130deg,440 rgba(251, 113, 133, 0.8),441 rgba(249, 115, 22, 0.8),442 rgba(163, 230, 53, 0.68),443 rgba(34, 211, 238, 0.78),444 rgba(167, 139, 250, 0.78)445 );446 opacity: 0;447 transition: opacity 260ms ease;448}449
450.nebula-frame::after {451 content: "";452 position: absolute;453 inset: -22px;454 z-index: -5;455 border-radius: 38px;456 background:457 radial-gradient(circle at 18% 10%, rgba(251, 113, 133, 0.26), transparent 34%),458 radial-gradient(circle at 78% 12%, rgba(34, 211, 238, 0.18), transparent 34%),459 radial-gradient(circle at 50% 100%, rgba(249, 115, 22, 0.18), transparent 36%);460 filter: blur(22px);461 opacity: 0.46;462 pointer-events: none;463}464
465.nebula-shell {466 position: relative;467 display: flex;468 align-items: center;469 min-height: 72px;470 padding: 0 108px 0 58px;471 border: 1px solid var(--nebula-line);472 border-radius: 28px;473 background:474 linear-gradient(180deg, rgba(255, 255, 255, 0.08), rgba(255, 255, 255, 0.025)),475 var(--nebula-panel);476 box-shadow:477 0 28px 90px rgba(0, 0, 0, 0.42),478 inset 0 1px 0 rgba(255, 255, 255, 0.11);479 overflow: hidden;480 cursor: text;481 transition:482 transform 220ms ease,483 border-color 220ms ease,484 box-shadow 220ms ease,485 background 220ms ease;486}487
488.nebula-shell::before {489 content: "";490 position: absolute;491 inset: 1px;492 z-index: -2;493 border-radius: 27px;494 background:495 radial-gradient(circle at 20% 0%, rgba(251, 113, 133, 0.17), transparent 34%),496 radial-gradient(circle at 84% 100%, rgba(34, 211, 238, 0.12), transparent 36%),497 linear-gradient(180deg, rgba(15, 23, 42, 0.98), rgba(8, 11, 20, 0.98));498}499
500.nebula-shell::after {501 content: "";502 position: absolute;503 left: -35%;504 top: 0;505 width: 34%;506 height: 100%;507 z-index: -1;508 background: linear-gradient(509 90deg,510 transparent,511 rgba(255, 255, 255, 0.12),512 transparent513 );514 transform: skewX(-18deg);515 opacity: 0;516}517
518.nebula-orb {519 position: absolute;520 z-index: -3;521 border-radius: 999px;522 filter: blur(18px);523 opacity: 0.45;524 pointer-events: none;525}526
527.orb-one {528 width: 126px;529 height: 126px;530 left: 18px;531 top: -24px;532 background: rgba(251, 113, 133, 0.45);533}534
535.orb-two {536 width: 148px;537 height: 148px;538 right: 34px;539 bottom: -48px;540 background: rgba(34, 211, 238, 0.35);541}542
543.orb-three {544 width: 96px;545 height: 96px;546 right: 108px;547 top: -30px;548 background: rgba(249, 115, 22, 0.36);549}550
551.nebula-search-icon {552 position: absolute;553 left: 22px;554 display: grid;555 place-items: center;556 width: 23px;557 height: 23px;558 color: var(--nebula-muted);559 transition:560 color 220ms ease,561 transform 220ms ease;562}563
564.nebula-search-icon svg,565.nebula-action svg,566.nebula-option-icon svg {567 width: 100%;568 height: 100%;569 fill: none;570 stroke: currentColor;571 stroke-width: 1.85;572 stroke-linecap: round;573 stroke-linejoin: round;574}575
576.nebula-label {577 position: absolute;578 left: 58px;579 top: 50%;580 color: var(--nebula-muted);581 font-size: 15px;582 font-weight: 760;583 letter-spacing: -0.015em;584 transform: translateY(-50%);585 transform-origin: left center;586 pointer-events: none;587 transition:588 transform 200ms ease,589 color 200ms ease,590 opacity 200ms ease;591}592
593.nebula-input {594 width: 100%;595 height: 72px;596 border: 0;597 outline: 0;598 background: transparent;599 color: var(--nebula-text);600 font-size: 16px;601 font-weight: 720;602 letter-spacing: -0.018em;603 caret-color: var(--nebula-orange);604}605
606.nebula-input::placeholder {607 color: rgba(148, 163, 184, 0.72);608 font-weight: 560;609}610
611.nebula-shortcut {612 position: absolute;613 right: 18px;614 display: inline-flex;615 align-items: center;616 justify-content: center;617 min-width: 44px;618 height: 30px;619 padding: 0 10px;620 border: 1px solid rgba(255, 255, 255, 0.11);621 border-radius: 12px;622 background:623 linear-gradient(180deg, rgba(255, 255, 255, 0.09), rgba(255, 255, 255, 0.035));624 color: rgba(226, 232, 240, 0.82);625 font-size: 12px;626 font-weight: 850;627 letter-spacing: -0.01em;628 box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08);629 transition:630 opacity 180ms ease,631 transform 180ms ease;632}633
634.nebula-action {635 position: absolute;636 right: 20px;637 display: grid;638 place-items: center;639 width: 34px;640 height: 34px;641 border: 1px solid rgba(255, 255, 255, 0.12);642 border-radius: 999px;643 background: rgba(255, 255, 255, 0.065);644 color: var(--nebula-muted);645 cursor: pointer;646 transition:647 color 180ms ease,648 background 180ms ease,649 border-color 180ms ease,650 transform 180ms ease;651}652
653.nebula-action:hover {654 color: var(--nebula-text);655 background: rgba(255, 255, 255, 0.12);656 transform: scale(1.05);657}658
659.nebula-action:focus-visible {660 outline: 3px solid rgba(249, 115, 22, 0.32);661 outline-offset: 3px;662}663
664.nebula-loader {665 position: absolute;666 right: 22px;667 width: 24px;668 height: 24px;669 border: 2px solid rgba(148, 163, 184, 0.25);670 border-top-color: var(--nebula-orange);671 border-right-color: var(--nebula-cyan);672 border-radius: 999px;673 animation: nebula-spin 760ms linear infinite;674}675
676.nebula-list {677 position: absolute;678 left: 10px;679 right: 10px;680 top: calc(100% + 10px);681 z-index: 20;682 display: grid;683 gap: 7px;684 padding: 9px;685 border: 1px solid var(--nebula-line);686 border-radius: 24px;687 background:688 linear-gradient(180deg, rgba(255, 255, 255, 0.08), rgba(255, 255, 255, 0.035)),689 rgba(11, 15, 27, 0.96);690 box-shadow:691 0 30px 90px rgba(0, 0, 0, 0.48),692 inset 0 1px 0 rgba(255, 255, 255, 0.08);693 backdrop-filter: blur(18px);694 transform-origin: top center;695 animation: nebula-list-in 170ms ease both;696}697
698.nebula-option {699 position: relative;700 display: flex;701 align-items: center;702 gap: 11px;703 width: 100%;704 min-height: 58px;705 padding: 10px 12px;706 border: 1px solid transparent;707 border-radius: 18px;708 background: transparent;709 color: var(--nebula-text);710 text-align: left;711 cursor: pointer;712 overflow: hidden;713 transition:714 background 160ms ease,715 border-color 160ms ease,716 transform 160ms ease;717}718
719.nebula-option::before {720 content: "";721 position: absolute;722 inset: 0;723 background:724 radial-gradient(circle at 12% 0%, rgba(251, 113, 133, 0.14), transparent 34%),725 radial-gradient(circle at 100% 100%, rgba(34, 211, 238, 0.11), transparent 36%);726 opacity: 0;727 transition: opacity 160ms ease;728}729
730.nebula-option:hover,731.nebula-option.is-active {732 border-color: rgba(255, 255, 255, 0.12);733 background: rgba(255, 255, 255, 0.07);734 transform: translateX(2px);735}736
737.nebula-option:hover::before,738.nebula-option.is-active::before {739 opacity: 1;740}741
742.nebula-option-icon {743 position: relative;744 display: grid;745 place-items: center;746 width: 34px;747 height: 34px;748 flex: 0 0 auto;749 border-radius: 13px;750 background:751 linear-gradient(135deg, rgba(251, 113, 133, 0.22), rgba(34, 211, 238, 0.14));752 color: rgba(248, 250, 252, 0.86);753}754
755.nebula-option-text {756 position: relative;757 display: grid;758 gap: 2px;759 min-width: 0;760}761
762.nebula-option-text strong {763 color: var(--nebula-text);764 font-size: 14px;765 font-weight: 820;766 letter-spacing: -0.012em;767}768
769.nebula-option-text small {770 color: var(--nebula-muted);771 font-size: 12.5px;772 font-weight: 560;773}774
775.nebula-status {776 display: flex;777 align-items: center;778 gap: 8px;779 min-height: 23px;780 margin: 11px 6px 0;781 color: var(--nebula-muted);782 font-size: 13px;783 line-height: 1.35;784}785
786.nebula-status-dot {787 width: 7px;788 height: 7px;789 flex: 0 0 auto;790 border-radius: 999px;791 background: currentColor;792 opacity: 0.72;793}794
795.nebula-command.is-focused .nebula-frame::before,796.nebula-command.is-open .nebula-frame::before {797 opacity: 1;798}799
800.nebula-command.is-focused .nebula-shell {801 border-color: transparent;802 box-shadow:803 0 34px 105px rgba(0, 0, 0, 0.5),804 0 0 0 5px rgba(249, 115, 22, 0.08),805 inset 0 1px 0 rgba(255, 255, 255, 0.14);806 transform: translateY(-1px);807}808
809.nebula-command.is-focused .nebula-shell::after {810 opacity: 1;811 animation: nebula-shine 1100ms ease;812}813
814.nebula-command.is-focused .nebula-search-icon {815 color: var(--nebula-orange);816 transform: scale(1.06);817}818
819.nebula-command.is-focused .nebula-label,820.nebula-command.has-value .nebula-label {821 color: rgba(248, 250, 252, 0.76);822 transform: translateY(-27px) scale(0.78);823}824
825.nebula-command.has-value .nebula-shortcut,826.nebula-command.is-loading .nebula-shortcut {827 opacity: 0;828 transform: scale(0.92);829 pointer-events: none;830}831
832.nebula-command.just-searched .nebula-shell {833 animation: nebula-confirm 520ms ease;834}835
836.nebula-command.is-disabled {837 opacity: 0.58;838}839
840.nebula-command.is-disabled .nebula-shell,841.nebula-command.is-disabled .nebula-input {842 cursor: not-allowed;843}844
845@keyframes nebula-spin {846 to {847 transform: rotate(360deg);848 }849}850
851@keyframes nebula-shine {852 from {853 left: -35%;854 }855
856 to {857 left: 130%;858 }859}860
861@keyframes nebula-list-in {862 from {863 opacity: 0;864 transform: translateY(-6px) scale(0.98);865 }866
867 to {868 opacity: 1;869 transform: translateY(0) scale(1);870 }871}872
873@keyframes nebula-confirm {874 0% {875 box-shadow:876 0 34px 105px rgba(0, 0, 0, 0.5),877 0 0 0 5px rgba(249, 115, 22, 0.08),878 inset 0 1px 0 rgba(255, 255, 255, 0.14);879 }880
881 45% {882 box-shadow:883 0 34px 105px rgba(0, 0, 0, 0.5),884 0 0 0 7px rgba(163, 230, 53, 0.14),885 inset 0 1px 0 rgba(255, 255, 255, 0.14);886 }887
888 100% {889 box-shadow:890 0 34px 105px rgba(0, 0, 0, 0.5),891 0 0 0 5px rgba(249, 115, 22, 0.08),892 inset 0 1px 0 rgba(255, 255, 255, 0.14);893 }894}895
896@media (max-width: 520px) {897 .nebula-shell {898 min-height: 68px;899 padding-right: 74px;900 }901
902 .nebula-shortcut {903 display: none;904 }905
906 .nebula-input {907 height: 68px;908 font-size: 15px;909 }910}911
912@media (prefers-reduced-motion: reduce) {913 .nebula-frame::before,914 .nebula-shell,915 .nebula-shell::after,916 .nebula-search-icon,917 .nebula-label,918 .nebula-shortcut,919 .nebula-action,920 .nebula-loader,921 .nebula-list,922 .nebula-option,923 .nebula-option::before {924 animation: none;925 transition: none;926 }927}928</style>Keine Installation, kein Build-Step — reine Standards, sofort einsatzbereit.
Klicke oben auf „Code kopieren“ — HTML und CSS landen in deiner Zwischenablage.
Füge das Markup an der gewünschten Stelle deiner Seite oder Komponente ein.
Übernimm den <style>-Block bzw. die Klassen in deine Stylesheet-Datei.
Aus derselben Kategorie oder mit passenden Tags.
Melde dich an, um zu kommentieren.