@@ -27,36 +27,39 @@ const id = useId()
2727<template >
2828 <label
2929 :for =" id"
30- class =" grid items-center gap-4 py-1 -my-1"
31- :class =" [
32- justify === 'start' ? 'justify-start' : '',
33- props.reverseOrder ? 'toggle-reverse' : 'toggle-default',
34- ]"
30+ class =" grid items-center gap-4 py-1 -my-1 grid-cols-[auto_1fr_auto]"
31+ :class =" [justify === 'start' ? 'justify-start' : '']"
32+ :style ="
33+ props.reverseOrder
34+ ? 'grid-template-areas: \'toggle . label-text\''
35+ : 'grid-template-areas: \'label-text . toggle\''
36+ "
3537 >
3638 <template v-if =" props .reverseOrder " >
3739 <input
3840 role =" switch"
3941 type =" checkbox"
4042 :id
41- class =" toggle-checkbox opacity-0"
42- style =" grid-row : 1 ; grid-column : 1 ; justify-self : start "
4343 v-model =" checked"
44+ class =" toggle appearance-none h-6 w-11 rounded-full border border-fg relative shrink-0 cursor-pointer 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"
4446 />
45- <span
46- class =" toggle-background h-6 w-11 rounded-full border border-fg relative flex shrink-0"
47- ></span >
4847 <TooltipApp
4948 v-if =" tooltip && label"
5049 :text =" tooltip"
5150 :position =" tooltipPosition ?? 'top'"
5251 :to =" tooltipTo"
5352 :offset =" tooltipOffset"
5453 >
55- <span class =" text-sm text-fg font-medium text-start" >
54+ <span class =" text-sm text-fg font-medium text-start" style = " grid-area : label-text " >
5655 {{ label }}
5756 </span >
5857 </TooltipApp >
59- <span v-else-if =" label" class =" text-sm text-fg font-medium text-start" >
58+ <span
59+ v-else-if =" label"
60+ class =" text-sm text-fg font-medium text-start"
61+ style =" grid-area : label-text"
62+ >
6063 {{ label }}
6164 </span >
6265 </template >
@@ -68,25 +71,25 @@ const id = useId()
6871 :to =" tooltipTo"
6972 :offset =" tooltipOffset"
7073 >
71- <span class =" text-sm text-fg font-medium text-start" >
74+ <span class =" text-sm text-fg font-medium text-start" style = " grid-area : label-text " >
7275 {{ label }}
7376 </span >
7477 </TooltipApp >
75- <span v-else-if =" label" class =" text-sm text-fg font-medium text-start" >
78+ <span
79+ v-else-if =" label"
80+ class =" text-sm text-fg font-medium text-start"
81+ style =" grid-area : label-text"
82+ >
7683 {{ label }}
7784 </span >
7885 <input
7986 role =" switch"
8087 type =" checkbox"
8188 :id
82- class =" toggle-checkbox opacity-0"
83- style =" grid-row : 1 ; grid-column : 3 ; justify-self : end "
8489 v-model =" checked"
90+ class =" toggle appearance-none h-6 w-11 rounded-full border border-fg relative shrink-0 cursor-pointer 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 "
8592 />
86- <span
87- class =" toggle-background h-6 w-11 rounded-full border border-fg relative flex shrink-0"
88- style =" grid-area : toggle-background; justify-self : end "
89- ></span >
9093 </template >
9194 </label >
9295 <p v-if =" description" class =" text-sm text-fg-muted mt-2" >
@@ -95,76 +98,45 @@ const id = useId()
9598</template >
9699
97100<style scoped>
98- /* Layout: default order (label first, toggle last) */
99- .toggle-default {
100- grid-template-areas : ' label-text . toggle-background' ;
101- grid-template-columns : auto 1fr auto ;
102- }
103-
104- /* Layout: reverse order (toggle first, label last) */
105- .toggle-reverse {
106- grid-template-areas : ' toggle-background . label-text' ;
107- grid-template-columns : auto 1fr auto ;
101+ /* Thumb position: logical property for RTL support */
102+ .toggle ::before {
103+ inset-inline-start : 1px ;
108104}
109105
110- /* Track background */
111- .toggle-background {
112- background : var (--fg-subtle );
106+ /* Track transition */
107+ .toggle {
113108 transition :
114109 background-color 100ms ease-in ,
115110 border-color 100ms ease-in ;
116111}
117112
118- @media (prefers-reduced-motion: reduce) {
119- .toggle-background {
120- transition : none ;
121- }
122- }
123-
124- label :has (input :focus-visible ) .toggle-background {
125- outline : solid 2px var (--fg );
126- outline-offset : 2px ;
127- }
128-
129- label :has (input :checked ) .toggle-background {
130- background : var (--fg );
131- border-color : var (--fg );
113+ .toggle ::before {
114+ transition : translate 200ms ease-in-out ;
132115}
133116
134- label :has (input :hover:not (:checked )) .toggle-background {
117+ /* Hover states */
118+ .toggle :hover:not (:checked ) {
135119 background : var (--fg-muted );
136120}
137121
138- label :has ( input : checked:hover) .toggle-background {
122+ .toggle : checked:hover {
139123 background : var (--fg-muted );
140124 border-color : var (--fg-muted );
141125}
142126
143- /* Circle that moves */
144- .toggle-background ::before {
145- transition : translate 200ms ease-in-out ;
146- content : ' ' ;
147- width : 20px ;
148- height : 20px ;
149- top : 1px ;
150- inset-inline-start : 1px ;
151- position : absolute ;
152- border-radius : 9999px ;
153- background : var (--bg );
127+ /* RTL-aware checked thumb position */
128+ :dir (ltr ) .toggle :checked ::before {
129+ translate : 20px ;
130+ }
131+
132+ :dir (rtl ) .toggle :checked ::before {
133+ translate : -20px ;
154134}
155135
156136@media (prefers-reduced-motion: reduce) {
157- .toggle-background ::before {
137+ .toggle ,
138+ .toggle ::before {
158139 transition : none ;
159140 }
160141}
161-
162- /* Support rtl locales */
163- :dir (ltr ) input :checked + .toggle-background ::before {
164- translate : 20px ;
165- }
166-
167- :dir (rtl ) input :checked + .toggle-background ::before {
168- translate : -20px ;
169- }
170142 </style >
0 commit comments