@@ -27,23 +27,39 @@ const id = useId()
2727<template >
2828 <label
2929 :for =" id"
30- class =" grid items-center gap-1.5 py-1 -my-1 grid-cols-[auto_1fr_auto]"
30+ class =" grid items-center gap-1.5 py-1 -my-1 grid-cols-[auto_1fr_auto] cursor-pointer group "
3131 :class =" [justify === 'start' ? 'justify-start' : '']"
3232 :style ="
3333 props.reverseOrder
3434 ? 'grid-template-areas: \'toggle . label-text\''
3535 : 'grid-template-areas: \'label-text . toggle\''
3636 "
3737 >
38+ <span
39+ class =" relative inline-flex items-center shrink-0"
40+ style =" grid-area : toggle; justify-self : end "
41+ >
42+ <input type =" checkbox" :id =" id" role =" switch" v-model =" checked" class =" sr-only peer" />
43+
44+ <!-- Track -->
45+ <span
46+ class =" w-11 h-6 bg-fg-subtle peer-focus:outline-none peer-focus-visible:ring-2 peer-focus-visible:ring-fg peer-focus-visible:ring-offset-2 rounded-full peer peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-bg after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-fg transition-colors duration-200 ease-in-out"
47+ ></span >
48+
49+ <!-- Thumb Icon (Check) - Positioned absolutely over the thumb area -->
50+ <span
51+ class =" absolute top-[2px] start-[2px] h-5 w-5 rounded-full flex items-center justify-center transition-transform duration-200 ease-in-out pointer-events-none"
52+ :class =" checked ? 'translate-x-5 rtl:-translate-x-5' : 'translate-x-0'"
53+ >
54+ <span
55+ class =" i-lucide:check w-3.5 h-3.5 text-fg transition-transform duration-200"
56+ :class =" checked ? 'scale-100 opacity-100' : 'scale-0 opacity-0'"
57+ aria-hidden =" true"
58+ ></span >
59+ </span >
60+ </span >
61+
3862 <template v-if =" props .reverseOrder " >
39- <input
40- role =" switch"
41- type =" checkbox"
42- :id
43- v-model =" checked"
44- class =" toggle appearance-none h-6 w-11 rounded-full border border-fg relative shrink-0 bg-fg-subtle checked:bg-fg checked:border-fg focus-visible:(outline-2 outline-fg outline-offset-2) before:content-[''] before:absolute before:h-5 before:w-5 before:top-1px before:rounded-full before:bg-bg"
45- style =" grid-area : toggle"
46- />
4763 <TooltipApp
4864 v-if =" tooltip && label"
4965 :text =" tooltip"
@@ -82,14 +98,6 @@ const id = useId()
8298 >
8399 {{ label }}
84100 </span >
85- <input
86- role =" switch"
87- type =" checkbox"
88- :id
89- v-model =" checked"
90- class =" toggle appearance-none h-6 w-11 rounded-full border border-fg relative shrink-0 bg-fg-subtle checked:bg-fg checked:border-fg focus-visible:(outline-2 outline-fg outline-offset-2) before:content-[''] before:absolute before:h-5 before:w-5 before:top-1px before:rounded-full before:bg-bg"
91- style =" grid-area : toggle; justify-self : end "
92- />
93101 </template >
94102 </label >
95103 <p v-if =" description" class =" text-sm text-fg-muted mt-2" >
@@ -98,82 +106,31 @@ const id = useId()
98106</template >
99107
100108<style scoped>
101- /* Thumb position: logical property for RTL support */
102- .toggle ::before {
103- inset-inline-start : 1px ;
104- }
105-
106- /* Track transition */
107- .toggle {
108- transition :
109- background-color 200ms ease-in-out ,
110- border-color 100ms ease-in-out ;
111- }
112-
113- .toggle ::before {
114- transition :
115- background-color 200ms ease-in-out ,
116- translate 200ms ease-in-out ;
117- }
118-
119- /* Hover states */
120- .toggle :hover:not (:checked ) {
121- background : var (--fg-muted );
122- }
123-
124- .toggle :checked:hover {
125- background : var (--fg-muted );
126- border-color : var (--fg-muted );
127- }
128-
129- /* RTL-aware checked thumb position */
130- :dir (ltr ) .toggle :checked ::before {
131- translate : 20px ;
132- }
133-
134- :dir (rtl ) .toggle :checked ::before {
135- translate : -20px ;
136- }
137-
138- @media (prefers-reduced-motion: reduce) {
139- .toggle ,
140- .toggle ::before {
141- transition : none ;
142- }
143- }
144-
145109/* Support forced colors */
146110@media (forced-colors: active) {
147- label > span {
148- background : Canvas;
149- color : Highlight ;
150- forced-color-adjust : none ;
151- }
152-
153- label :has (.toggle :checked ) > span {
111+ /* Track */
112+ input :checked + span {
154113 background : Highlight ;
155- color : Canvas;
156- }
157-
158- .toggle ::before {
159- forced-color-adjust : none ;
160- background-color : Highlight ;
161114 }
162115
163- .toggle ,
164- .toggle :hover {
116+ /* Thumb border/bg */
117+ input :checked + span ::after {
165118 background : Canvas;
166119 border-color : CanvasText;
167120 }
168121
169- .toggle :checked ,
170- .toggle :checked:hover {
171- background : Highlight ;
172- border-color : CanvasText;
122+ /* Icon */
123+ .i-lucide\: check {
124+ color : Highlight ;
173125 }
126+ }
174127
175- .toggle :checked ::before {
176- background : Canvas;
128+ @media (prefers-reduced-motion: reduce) {
129+ span ,
130+ span ::after ,
131+ .i-lucide\: check {
132+ transition : none !important ;
133+ animation : none !important ;
177134 }
178135}
179136 </style >
0 commit comments