wip bsky client for the web & android
bbell.vt3e.cat
1<script setup lang="ts">
2import { useId, useSlots } from 'vue'
3
4defineProps<{
5 label?: string
6 placeholder?: string
7 type?: 'text' | 'password' | 'email' | 'number' | 'search'
8 error?: string
9}>()
10
11const model = defineModel<string | number>()
12const id = useId()
13const slots = useSlots()
14</script>
15
16<template>
17 <div class="input-group">
18 <label v-if="label" :for="id" class="label">
19 {{ label }}
20 </label>
21 <div class="input-wrapper">
22 <div v-if="slots.prefix" class="prefix-wrapper">
23 <slot name="prefix"></slot>
24 </div>
25 <input
26 :id="id"
27 v-model="model"
28 :type="type || 'text'"
29 :placeholder="placeholder"
30 :class="{ input: true, 'has-prefix': !!slots.prefix, 'has-error': !!error }"
31 />
32 </div>
33 <span v-if="error" class="error-text">{{ error }}</span>
34 </div>
35</template>
36
37<style scoped lang="scss">
38.input-group {
39 display: flex;
40 flex-direction: column;
41 gap: 0.375rem;
42 width: 100%;
43 margin-bottom: 1rem;
44}
45
46.label {
47 font-size: 0.875rem;
48 font-weight: 600;
49 color: hsl(var(--text));
50 margin-left: 0.125rem;
51}
52
53.input-wrapper {
54 position: relative;
55 width: 100%;
56
57 display: flex;
58 align-items: center;
59
60 border-radius: var(--radius-md);
61 border: 1px solid hsla(var(--surface2) / 0.5);
62 background-color: hsl(var(--surface0) / 0.3);
63
64 outline: 2px solid transparent;
65 outline-offset: 4px;
66
67 &:hover {
68 background-color: hsl(var(--surface0));
69 border-color: hsl(var(--surface2));
70 }
71 &:focus-within {
72 background-color: hsl(var(--base));
73 outline-color: hsl(var(--accent));
74 outline-offset: 2px;
75 }
76 &:has(.has-error) {
77 border-color: hsl(var(--red));
78 color: hsl(var(--red));
79 &:focus-visible {
80 box-shadow: 0 0 0 3px hsla(var(--red) / 0.15);
81 }
82 }
83}
84
85.input {
86 color: hsl(var(--text));
87 font-size: 1rem;
88 font-family: inherit;
89 background: transparent;
90 border: none;
91 width: 100%;
92 padding: 0.75rem 1rem;
93
94 outline: none;
95
96 &.has-prefix {
97 padding-left: 0;
98 }
99
100 &:focus-visible {
101 outline: none;
102 }
103
104 &::placeholder {
105 color: hsl(var(--overlay0));
106 }
107}
108
109.prefix-wrapper {
110 height: 100%;
111 display: flex;
112 align-items: center;
113 color: hsl(var(--overlay0));
114 min-width: 2.5rem;
115 justify-content: center;
116}
117
118.error-text {
119 font-size: 0.75rem;
120 color: hsl(var(--red));
121 font-weight: 600;
122 margin-left: 0.125rem;
123}
124</style>