maquina-components 0.6.1 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +2 -1
- data/app/assets/stylesheets/badge.css +3 -2
- data/app/assets/stylesheets/breadcrumbs.css +42 -10
- data/app/assets/stylesheets/calendar.css +8 -6
- data/app/assets/stylesheets/card.css +3 -2
- data/app/assets/stylesheets/combobox.css +10 -1
- data/app/assets/stylesheets/date_picker.css +7 -6
- data/app/assets/stylesheets/drawer.css +4 -3
- data/app/assets/stylesheets/dropdown_menu.css +1 -1
- data/app/assets/stylesheets/form.css +83 -34
- data/app/assets/stylesheets/menu_button.css +3 -2
- data/app/assets/stylesheets/pagination.css +4 -3
- data/app/assets/stylesheets/sidebar.css +42 -18
- data/app/assets/stylesheets/toast.css +7 -5
- data/app/assets/stylesheets/toggle_group.css +4 -3
- data/app/helpers/maquina_components/breadcrumbs_helper.rb +10 -2
- data/app/helpers/maquina_components/icons_helper.rb +35 -3
- data/app/javascript/controllers/breadcrumb_controller.js +97 -40
- data/app/javascript/controllers/dropdown_menu_controller.js +46 -1
- data/app/javascript/controllers/menu_button_controller.js +38 -0
- data/app/javascript/controllers/sidebar_controller.js +23 -0
- data/app/views/components/_breadcrumbs.html.erb +3 -7
- data/app/views/components/_sidebar.html.erb +4 -0
- data/app/views/components/alert/_description.html.erb +1 -1
- data/app/views/components/alert/_title.html.erb +1 -1
- data/app/views/components/card/_description.html.erb +1 -1
- data/app/views/components/card/_title.html.erb +1 -1
- data/app/views/components/combobox/_label.html.erb +1 -1
- data/app/views/components/combobox/_trigger.html.erb +1 -1
- data/app/views/components/drawer/_description.html.erb +1 -1
- data/app/views/components/drawer/_title.html.erb +1 -1
- data/app/views/components/sidebar/_menu_badge.html.erb +1 -1
- data/app/views/components/table/_caption.html.erb +1 -1
- data/app/views/components/table/_cell.html.erb +1 -1
- data/app/views/components/table/_head.html.erb +1 -1
- data/app/views/components/toast/_description.html.erb +1 -1
- data/app/views/components/toast/_title.html.erb +1 -1
- data/lib/maquina_components/doctor.rb +208 -14
- data/lib/maquina_components/version.rb +1 -1
- data/lib/tasks/maquina_components_tasks.rake +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ca1b928bf464480638e3f7504164a10cfecab249eed3a970dbaaad5a3d5b311a
|
|
4
|
+
data.tar.gz: 27998f7d8cdf85c6e49af5b4db527086093149130af50a722105e8970c01838c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8105dbdafd0bbf1f1be19ff9ba7f920f42f79b74703098e4229131b5310fb50cbd7e3ecdbf2af2cecfcd7f2f241f15be247e8592d5534995ed3cba931de50e7e
|
|
7
|
+
data.tar.gz: 31286d951a265d58bb22021f625242ce36e612ce9d0fe6a8a8ac3c057d0f845d34ea240ba1d2cf15aa4a87a33be8592bee5e1c8483a26b4bc733b069528ba27e
|
data/README.md
CHANGED
|
@@ -165,7 +165,8 @@ bin/rails maquina:doctor
|
|
|
165
165
|
```
|
|
166
166
|
|
|
167
167
|
It scans your CSS, views and JavaScript and prints file:line for every pattern
|
|
168
|
-
the new release changes, grouped `BREAKING` / `REVIEW` / `CLEANUP
|
|
168
|
+
the new release changes, grouped `BREAKING` / `REVIEW` / `CLEANUP`, each tagged
|
|
169
|
+
with the release it came from. It is
|
|
169
170
|
advisory: it never edits anything and never fails a build. See
|
|
170
171
|
[docs/upgrading.md](docs/upgrading.md) for what changed and how to keep the
|
|
171
172
|
previous look.
|
|
@@ -133,8 +133,9 @@
|
|
|
133
133
|
|
|
134
134
|
/* Focus state for keyboard navigation */
|
|
135
135
|
[data-component="badge"]:where(a:focus-visible, button:focus-visible) {
|
|
136
|
-
outline: var(--focus-ring-width, 3px)
|
|
137
|
-
|
|
136
|
+
outline-width: var(--focus-ring-width, 3px);
|
|
137
|
+
outline-style: var(--focus-ring-style, solid);
|
|
138
|
+
outline-color: var(--focus-ring-color, var(--ring));
|
|
138
139
|
outline-offset: var(--focus-ring-offset, 0px);
|
|
139
140
|
}
|
|
140
141
|
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
@apply text-sm;
|
|
56
56
|
font-weight: var(--label-weight, 500);
|
|
57
57
|
@apply underline-offset-4;
|
|
58
|
-
@apply transition-
|
|
58
|
+
@apply transition-[color,background-color,border-color,text-decoration-color];
|
|
59
59
|
color: var(--muted-foreground);
|
|
60
60
|
}
|
|
61
61
|
|
|
@@ -73,9 +73,16 @@
|
|
|
73
73
|
@apply underline;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
/* The underline is kept, but it cannot be the *only* signal: on a page whose
|
|
77
|
+
breadcrumbs are the sole keyboard stops, a text-decoration change reads as
|
|
78
|
+
nothing happening. Ring first, underline as reinforcement. */
|
|
76
79
|
[data-breadcrumb-part="link"]:where(:focus-visible) {
|
|
77
|
-
@apply
|
|
80
|
+
@apply underline;
|
|
78
81
|
color: var(--foreground);
|
|
82
|
+
outline-width: var(--focus-ring-width, 3px);
|
|
83
|
+
outline-style: var(--focus-ring-style, solid);
|
|
84
|
+
outline-color: var(--focus-ring-color, var(--ring));
|
|
85
|
+
outline-offset: var(--focus-ring-offset, 0px);
|
|
79
86
|
}
|
|
80
87
|
|
|
81
88
|
/* ===== Breadcrumb Page (current) ===== */
|
|
@@ -126,6 +133,15 @@
|
|
|
126
133
|
color: var(--foreground);
|
|
127
134
|
}
|
|
128
135
|
|
|
136
|
+
/* It is a trigger, so it is a tab stop, so it rings. */
|
|
137
|
+
[data-breadcrumb-part="ellipsis"]:where(:focus-visible) {
|
|
138
|
+
color: var(--foreground);
|
|
139
|
+
outline-width: var(--focus-ring-width, 3px);
|
|
140
|
+
outline-style: var(--focus-ring-style, solid);
|
|
141
|
+
outline-color: var(--focus-ring-color, var(--ring));
|
|
142
|
+
outline-offset: var(--focus-ring-offset, 0px);
|
|
143
|
+
}
|
|
144
|
+
|
|
129
145
|
/* ===== Overflow Dropdown (top-layer popover) ===== */
|
|
130
146
|
/* Visual styling comes from dropdown_menu.css via data-dropdown-menu-part.
|
|
131
147
|
Anchor positioning places the popover in modern browsers;
|
|
@@ -156,17 +172,38 @@
|
|
|
156
172
|
@apply shrink-0 whitespace-nowrap;
|
|
157
173
|
}
|
|
158
174
|
|
|
159
|
-
/*
|
|
175
|
+
/* The last item (current page) is the overflow sponge of last resort: when a
|
|
176
|
+
single long title cannot be helped by collapsing anything, it truncates
|
|
177
|
+
rather than pushing the bar out of its box. */
|
|
160
178
|
:where([data-controller="breadcrumb"]) [data-breadcrumb-part="item"]:where(:last-child) {
|
|
161
179
|
@apply shrink min-w-0 overflow-hidden;
|
|
162
|
-
text-overflow: ellipsis;
|
|
163
180
|
}
|
|
164
181
|
|
|
182
|
+
/* display:block, not the inline-flex the other parts get. text-overflow has
|
|
183
|
+
no effect on a flex container's own box, so this rule used to hard-clip
|
|
184
|
+
mid-word -- "A Very Long Current Pag" -- while claiming to truncate
|
|
185
|
+
gracefully. A block box renders the ellipsis. An icon inside still flows
|
|
186
|
+
inline and keeps its margin. */
|
|
165
187
|
:where([data-controller="breadcrumb"] [data-breadcrumb-part="item"]:last-child) :is([data-breadcrumb-part="page"], [data-breadcrumb-part="link"]) {
|
|
166
|
-
@apply overflow-hidden;
|
|
188
|
+
@apply block overflow-hidden whitespace-nowrap;
|
|
167
189
|
text-overflow: ellipsis;
|
|
168
190
|
}
|
|
169
191
|
|
|
192
|
+
/* Measuring mode, toggled by breadcrumb_controller.js for the length of one
|
|
193
|
+
measurement. The sponge above is exactly what makes scrollWidth useless:
|
|
194
|
+
flex resolves a deficit by shrinking a shrinkable item *before* it lets the
|
|
195
|
+
line overflow, so the label absorbs everything down to 0px and
|
|
196
|
+
scrollWidth === clientWidth at every width. Measured: content wanting 430px
|
|
197
|
+
in a 300px box reported no overflow at all, which is why the width-based
|
|
198
|
+
collapse never fired and collapse_after had to be invented to fake it.
|
|
199
|
+
Pinning the last item to its natural width for the measurement makes the
|
|
200
|
+
list report the space it actually wants. */
|
|
201
|
+
:where([data-controller="breadcrumb"]) [data-breadcrumb-part="list"].breadcrumb-measuring [data-breadcrumb-part="item"]:where(:last-child) {
|
|
202
|
+
flex-shrink: 0;
|
|
203
|
+
min-width: max-content;
|
|
204
|
+
overflow: visible;
|
|
205
|
+
}
|
|
206
|
+
|
|
170
207
|
/* Items hidden by Stimulus controller */
|
|
171
208
|
:where([data-controller="breadcrumb"]) [data-breadcrumb-target="item"]:where(.hidden),
|
|
172
209
|
:where([data-controller="breadcrumb"]) [data-breadcrumb-target="ellipsisSeparator"]:where(.hidden) {
|
|
@@ -183,11 +220,6 @@
|
|
|
183
220
|
[data-breadcrumb-part="list"] {
|
|
184
221
|
@apply gap-1;
|
|
185
222
|
}
|
|
186
|
-
|
|
187
|
-
/* Hide middle items on very small screens by default */
|
|
188
|
-
:where([data-component="breadcrumbs"][data-auto-collapse]) [data-breadcrumb-part="item"]:where(:not(:first-child):not(:last-child)) {
|
|
189
|
-
@apply hidden sm:inline-flex;
|
|
190
|
-
}
|
|
191
223
|
}
|
|
192
224
|
|
|
193
225
|
/* ===== Separator Variants ===== */
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
cursor: pointer;
|
|
66
66
|
background-color: transparent;
|
|
67
67
|
color: var(--foreground);
|
|
68
|
-
@apply transition-
|
|
68
|
+
@apply transition-[color,background-color,border-color,text-decoration-color] duration-150;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
[data-calendar-part="header"] button svg {
|
|
@@ -78,8 +78,9 @@
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
[data-calendar-part="header"] button:where(:focus-visible) {
|
|
81
|
-
outline: var(--focus-ring-width, 3px)
|
|
82
|
-
|
|
81
|
+
outline-width: var(--focus-ring-width, 3px);
|
|
82
|
+
outline-style: var(--focus-ring-style, solid);
|
|
83
|
+
outline-color: var(--focus-ring-color, var(--ring));
|
|
83
84
|
outline-offset: var(--focus-ring-offset, 0px);
|
|
84
85
|
}
|
|
85
86
|
|
|
@@ -135,7 +136,7 @@
|
|
|
135
136
|
cursor: pointer;
|
|
136
137
|
background-color: transparent;
|
|
137
138
|
color: var(--foreground);
|
|
138
|
-
@apply transition-
|
|
139
|
+
@apply transition-[color,background-color,border-color,text-decoration-color] duration-150;
|
|
139
140
|
}
|
|
140
141
|
|
|
141
142
|
/* ===== Day Variants ===== */
|
|
@@ -228,8 +229,9 @@
|
|
|
228
229
|
[data-calendar-part="day"]:where(:focus-visible) {
|
|
229
230
|
position: relative;
|
|
230
231
|
z-index: 10;
|
|
231
|
-
outline: var(--focus-ring-width, 3px)
|
|
232
|
-
|
|
232
|
+
outline-width: var(--focus-ring-width, 3px);
|
|
233
|
+
outline-style: var(--focus-ring-style, solid);
|
|
234
|
+
outline-color: var(--focus-ring-color, var(--ring));
|
|
233
235
|
outline-offset: var(--focus-ring-offset, 0px);
|
|
234
236
|
}
|
|
235
237
|
}
|
|
@@ -127,8 +127,9 @@
|
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
[data-component="card"]:where(a:focus-visible, button:focus-visible) {
|
|
130
|
-
outline: var(--focus-ring-width, 3px)
|
|
131
|
-
|
|
130
|
+
outline-width: var(--focus-ring-width, 3px);
|
|
131
|
+
outline-style: var(--focus-ring-style, solid);
|
|
132
|
+
outline-color: var(--focus-ring-color, var(--ring));
|
|
132
133
|
outline-offset: var(--focus-ring-offset, 0px);
|
|
133
134
|
}
|
|
134
135
|
}
|
|
@@ -159,6 +159,15 @@
|
|
|
159
159
|
@apply disabled:cursor-not-allowed disabled:opacity-50;
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
+
/* The search field is a real tab stop -- the popover opens onto it -- so the
|
|
163
|
+
`outline-none` above has to be paid back here, like every other field. */
|
|
164
|
+
[data-combobox-part="input"]:where(:focus-visible) {
|
|
165
|
+
outline-width: var(--focus-ring-width, 3px);
|
|
166
|
+
outline-style: var(--focus-ring-style, solid);
|
|
167
|
+
outline-color: var(--focus-ring-color, color-mix(in oklch, var(--ring) 50%, transparent));
|
|
168
|
+
outline-offset: var(--focus-ring-offset, 0px);
|
|
169
|
+
}
|
|
170
|
+
|
|
162
171
|
/* ===== Options List ===== */
|
|
163
172
|
[data-combobox-part="list"] {
|
|
164
173
|
@apply max-h-[300px] overflow-y-auto overflow-x-hidden p-1;
|
|
@@ -172,7 +181,7 @@
|
|
|
172
181
|
cursor: default;
|
|
173
182
|
user-select: none;
|
|
174
183
|
color: var(--popover-foreground, var(--foreground));
|
|
175
|
-
@apply gap-2 py-1.5 text-sm outline-none transition-
|
|
184
|
+
@apply gap-2 py-1.5 text-sm outline-none transition-[color,background-color,border-color,text-decoration-color];
|
|
176
185
|
border-radius: var(--combobox-item-radius, var(--control-radius, 0.375rem));
|
|
177
186
|
@apply pl-8 pr-2; /* Space for check icon on left */
|
|
178
187
|
}
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
color: var(--foreground);
|
|
49
49
|
cursor: pointer;
|
|
50
50
|
text-align: left;
|
|
51
|
-
@apply transition-
|
|
51
|
+
@apply transition-[color,background-color,border-color,text-decoration-color] duration-150;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
[data-date-picker-part="trigger"] svg {
|
|
@@ -102,8 +102,9 @@
|
|
|
102
102
|
|
|
103
103
|
[data-date-picker-part="trigger"]:where(:focus-visible) {
|
|
104
104
|
border-color: var(--ring);
|
|
105
|
-
outline: var(--focus-ring-width, 3px)
|
|
106
|
-
|
|
105
|
+
outline-width: var(--focus-ring-width, 3px);
|
|
106
|
+
outline-style: var(--focus-ring-style, solid);
|
|
107
|
+
outline-color: var(--focus-ring-color, var(--ring));
|
|
107
108
|
outline-offset: var(--focus-ring-offset, 0px);
|
|
108
109
|
}
|
|
109
110
|
|
|
@@ -112,11 +113,11 @@
|
|
|
112
113
|
}
|
|
113
114
|
|
|
114
115
|
/* Error state — the root :has() drives the trigger */
|
|
115
|
-
:where([data-component="date-picker"]:has(input:invalid), [data-component="date-picker"]:has(input[aria-invalid="true"])) [data-date-picker-part="trigger"] {
|
|
116
|
-
border-color: var(--destructive);
|
|
116
|
+
:where([data-component="date-picker"]:has(input:user-invalid), [data-component="date-picker"]:has(input[aria-invalid="true"])) [data-date-picker-part="trigger"] {
|
|
117
|
+
border-color: var(--destructive-border, var(--destructive));
|
|
117
118
|
}
|
|
118
119
|
|
|
119
|
-
:where([data-component="date-picker"]:has(input:invalid), [data-component="date-picker"]:has(input[aria-invalid="true"])) [data-date-picker-part="trigger"]:where(:focus-visible) {
|
|
120
|
+
:where([data-component="date-picker"]:has(input:user-invalid), [data-component="date-picker"]:has(input[aria-invalid="true"])) [data-date-picker-part="trigger"]:where(:focus-visible) {
|
|
120
121
|
--focus-ring-color: var(--destructive);
|
|
121
122
|
}
|
|
122
123
|
|
|
@@ -114,7 +114,7 @@
|
|
|
114
114
|
/* ===== Drawer Close Button ===== */
|
|
115
115
|
[data-drawer-part="close"] {
|
|
116
116
|
@apply inline-flex items-center justify-center p-1;
|
|
117
|
-
@apply transition-
|
|
117
|
+
@apply transition-[color,background-color,border-color,text-decoration-color];
|
|
118
118
|
border-radius: var(--drawer-close-radius, var(--control-radius, 0.375rem));
|
|
119
119
|
color: var(--foreground);
|
|
120
120
|
}
|
|
@@ -130,8 +130,9 @@
|
|
|
130
130
|
|
|
131
131
|
/* outline, not a ring: the drawer panel clips box-shadow rings */
|
|
132
132
|
[data-drawer-part="close"]:where(:focus-visible) {
|
|
133
|
-
outline: var(--focus-ring-width, 3px)
|
|
134
|
-
|
|
133
|
+
outline-width: var(--focus-ring-width, 3px);
|
|
134
|
+
outline-style: var(--focus-ring-style, solid);
|
|
135
|
+
outline-color: var(--focus-ring-color, var(--ring));
|
|
135
136
|
outline-offset: var(--focus-ring-offset, 0px);
|
|
136
137
|
}
|
|
137
138
|
|
|
@@ -149,7 +149,7 @@
|
|
|
149
149
|
cursor: default;
|
|
150
150
|
user-select: none;
|
|
151
151
|
color: var(--popover-foreground, var(--foreground));
|
|
152
|
-
@apply gap-2 px-2 py-1.5 text-sm outline-none transition-
|
|
152
|
+
@apply gap-2 px-2 py-1.5 text-sm outline-none transition-[color,background-color,border-color,text-decoration-color];
|
|
153
153
|
border-radius: var(--dropdown-menu-item-radius, var(--control-radius, 0.375rem));
|
|
154
154
|
}
|
|
155
155
|
|
|
@@ -95,7 +95,7 @@
|
|
|
95
95
|
@apply border px-3 py-1;
|
|
96
96
|
@apply text-base md:text-sm;
|
|
97
97
|
@apply outline-none;
|
|
98
|
-
@apply transition-[color,box-shadow
|
|
98
|
+
@apply transition-[color,box-shadow];
|
|
99
99
|
|
|
100
100
|
border-radius: var(--input-radius, var(--control-radius, 0.375rem));
|
|
101
101
|
background-color: var(--control-fill, var(--maquina-control-fill-default, transparent));
|
|
@@ -141,8 +141,9 @@
|
|
|
141
141
|
/* Focus state — keyboard only. The bare :focus used to ring on mouse click. */
|
|
142
142
|
[data-component="input"]:where(:focus-visible) {
|
|
143
143
|
border-color: var(--ring);
|
|
144
|
-
outline: var(--focus-ring-width, 3px)
|
|
145
|
-
|
|
144
|
+
outline-width: var(--focus-ring-width, 3px);
|
|
145
|
+
outline-style: var(--focus-ring-style, solid);
|
|
146
|
+
outline-color: var(--focus-ring-color, color-mix(in oklch, var(--ring) 50%, transparent));
|
|
146
147
|
outline-offset: var(--focus-ring-offset, 0px);
|
|
147
148
|
}
|
|
148
149
|
|
|
@@ -153,11 +154,18 @@
|
|
|
153
154
|
|
|
154
155
|
/* Invalid/error state — the destructive halo is themeable through
|
|
155
156
|
--focus-ring-color, which the :focus-visible rule above also reads. */
|
|
156
|
-
[data-component="input"]:where([aria-invalid="true"], :invalid
|
|
157
|
+
[data-component="input"]:where([aria-invalid="true"], :user-invalid) {
|
|
157
158
|
--focus-ring-color: color-mix(in oklch, var(--destructive) 20%, transparent);
|
|
158
|
-
border-color: var(--destructive);
|
|
159
|
-
|
|
160
|
-
|
|
159
|
+
border-color: var(--destructive-border, var(--destructive));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/* The destructive halo is a focus affordance, not a resting one: painting an
|
|
163
|
+
outline on an unfocused field doubles the border and reads as a second
|
|
164
|
+
error. Gated to match the :focus-visible rule above. */
|
|
165
|
+
[data-component="input"]:where([aria-invalid="true"], :user-invalid):where(:focus-visible) {
|
|
166
|
+
outline-width: var(--focus-ring-width, 3px);
|
|
167
|
+
outline-style: var(--focus-ring-style, solid);
|
|
168
|
+
outline-color: var(--focus-ring-color, color-mix(in oklch, var(--destructive) 20%, transparent));
|
|
161
169
|
outline-offset: var(--focus-ring-offset, 0px);
|
|
162
170
|
}
|
|
163
171
|
|
|
@@ -178,7 +186,7 @@
|
|
|
178
186
|
@apply border px-3 py-2;
|
|
179
187
|
@apply text-base md:text-sm;
|
|
180
188
|
@apply outline-none resize-y;
|
|
181
|
-
@apply transition-[color,box-shadow
|
|
189
|
+
@apply transition-[color,box-shadow];
|
|
182
190
|
|
|
183
191
|
border-radius: var(--textarea-radius, var(--control-radius, 0.375rem));
|
|
184
192
|
background-color: var(--control-fill, var(--maquina-control-fill-default, transparent));
|
|
@@ -194,8 +202,9 @@
|
|
|
194
202
|
/* ----- States ----- */
|
|
195
203
|
[data-component="textarea"]:where(:focus-visible) {
|
|
196
204
|
border-color: var(--ring);
|
|
197
|
-
outline: var(--focus-ring-width, 3px)
|
|
198
|
-
|
|
205
|
+
outline-width: var(--focus-ring-width, 3px);
|
|
206
|
+
outline-style: var(--focus-ring-style, solid);
|
|
207
|
+
outline-color: var(--focus-ring-color, color-mix(in oklch, var(--ring) 50%, transparent));
|
|
199
208
|
outline-offset: var(--focus-ring-offset, 0px);
|
|
200
209
|
}
|
|
201
210
|
|
|
@@ -203,11 +212,18 @@
|
|
|
203
212
|
@apply cursor-not-allowed opacity-50;
|
|
204
213
|
}
|
|
205
214
|
|
|
206
|
-
[data-component="textarea"]:where([aria-invalid="true"], :invalid
|
|
215
|
+
[data-component="textarea"]:where([aria-invalid="true"], :user-invalid) {
|
|
207
216
|
--focus-ring-color: color-mix(in oklch, var(--destructive) 20%, transparent);
|
|
208
|
-
border-color: var(--destructive);
|
|
209
|
-
|
|
210
|
-
|
|
217
|
+
border-color: var(--destructive-border, var(--destructive));
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/* The destructive halo is a focus affordance, not a resting one: painting an
|
|
221
|
+
outline on an unfocused field doubles the border and reads as a second
|
|
222
|
+
error. Gated to match the :focus-visible rule above. */
|
|
223
|
+
[data-component="textarea"]:where([aria-invalid="true"], :user-invalid):where(:focus-visible) {
|
|
224
|
+
outline-width: var(--focus-ring-width, 3px);
|
|
225
|
+
outline-style: var(--focus-ring-style, solid);
|
|
226
|
+
outline-color: var(--focus-ring-color, color-mix(in oklch, var(--destructive) 20%, transparent));
|
|
211
227
|
outline-offset: var(--focus-ring-offset, 0px);
|
|
212
228
|
}
|
|
213
229
|
|
|
@@ -220,7 +236,7 @@
|
|
|
220
236
|
@apply border px-3 py-1 pr-8;
|
|
221
237
|
@apply text-base md:text-sm;
|
|
222
238
|
@apply outline-none appearance-none cursor-pointer;
|
|
223
|
-
@apply transition-[color,box-shadow
|
|
239
|
+
@apply transition-[color,box-shadow];
|
|
224
240
|
|
|
225
241
|
border-radius: var(--select-radius, var(--control-radius, 0.375rem));
|
|
226
242
|
background-color: var(--control-fill, var(--maquina-select-fill-default, var(--background)));
|
|
@@ -257,8 +273,9 @@
|
|
|
257
273
|
/* ----- States ----- */
|
|
258
274
|
[data-component="select"]:where(:focus-visible) {
|
|
259
275
|
border-color: var(--ring);
|
|
260
|
-
outline: var(--focus-ring-width, 3px)
|
|
261
|
-
|
|
276
|
+
outline-width: var(--focus-ring-width, 3px);
|
|
277
|
+
outline-style: var(--focus-ring-style, solid);
|
|
278
|
+
outline-color: var(--focus-ring-color, color-mix(in oklch, var(--ring) 50%, transparent));
|
|
262
279
|
outline-offset: var(--focus-ring-offset, 0px);
|
|
263
280
|
}
|
|
264
281
|
|
|
@@ -268,7 +285,7 @@
|
|
|
268
285
|
|
|
269
286
|
[data-component="select"]:where([aria-invalid="true"]) {
|
|
270
287
|
--focus-ring-color: color-mix(in oklch, var(--destructive) 20%, transparent);
|
|
271
|
-
border-color: var(--destructive);
|
|
288
|
+
border-color: var(--destructive-border, var(--destructive));
|
|
272
289
|
}
|
|
273
290
|
|
|
274
291
|
/* ===================================================================
|
|
@@ -338,8 +355,9 @@
|
|
|
338
355
|
|
|
339
356
|
[data-component="checkbox"]:where(:focus-visible) {
|
|
340
357
|
border-color: var(--ring);
|
|
341
|
-
outline: var(--focus-ring-width, 3px)
|
|
342
|
-
|
|
358
|
+
outline-width: var(--focus-ring-width, 3px);
|
|
359
|
+
outline-style: var(--focus-ring-style, solid);
|
|
360
|
+
outline-color: var(--focus-ring-color, color-mix(in oklch, var(--ring) 50%, transparent));
|
|
343
361
|
outline-offset: var(--focus-ring-offset, 0px);
|
|
344
362
|
}
|
|
345
363
|
|
|
@@ -349,9 +367,10 @@
|
|
|
349
367
|
|
|
350
368
|
[data-component="checkbox"]:where([aria-invalid="true"]) {
|
|
351
369
|
--focus-ring-color: color-mix(in oklch, var(--destructive) 20%, transparent);
|
|
352
|
-
border-color: var(--destructive);
|
|
353
|
-
outline: var(--focus-ring-width, 3px)
|
|
354
|
-
|
|
370
|
+
border-color: var(--destructive-border, var(--destructive));
|
|
371
|
+
outline-width: var(--focus-ring-width, 3px);
|
|
372
|
+
outline-style: var(--focus-ring-style, solid);
|
|
373
|
+
outline-color: var(--focus-ring-color, color-mix(in oklch, var(--destructive) 20%, transparent));
|
|
355
374
|
outline-offset: var(--focus-ring-offset, 0px);
|
|
356
375
|
}
|
|
357
376
|
|
|
@@ -392,8 +411,9 @@
|
|
|
392
411
|
|
|
393
412
|
[data-component="radio"]:where(:focus-visible) {
|
|
394
413
|
border-color: var(--ring);
|
|
395
|
-
outline: var(--focus-ring-width, 3px)
|
|
396
|
-
|
|
414
|
+
outline-width: var(--focus-ring-width, 3px);
|
|
415
|
+
outline-style: var(--focus-ring-style, solid);
|
|
416
|
+
outline-color: var(--focus-ring-color, color-mix(in oklch, var(--ring) 50%, transparent));
|
|
397
417
|
outline-offset: var(--focus-ring-offset, 0px);
|
|
398
418
|
}
|
|
399
419
|
|
|
@@ -435,8 +455,9 @@
|
|
|
435
455
|
}
|
|
436
456
|
|
|
437
457
|
[data-component="switch"]:where(:focus-visible) {
|
|
438
|
-
outline: var(--focus-ring-width, 3px)
|
|
439
|
-
|
|
458
|
+
outline-width: var(--focus-ring-width, 3px);
|
|
459
|
+
outline-style: var(--focus-ring-style, solid);
|
|
460
|
+
outline-color: var(--focus-ring-color, color-mix(in oklch, var(--ring) 50%, transparent));
|
|
440
461
|
outline-offset: var(--focus-ring-offset, 0px);
|
|
441
462
|
}
|
|
442
463
|
|
|
@@ -474,7 +495,15 @@
|
|
|
474
495
|
@apply h-9 px-4 py-2;
|
|
475
496
|
@apply cursor-default;
|
|
476
497
|
@apply outline-none;
|
|
477
|
-
|
|
498
|
+
/* NOT `transition-colors`. Tailwind v4 folds outline-color into that
|
|
499
|
+
utility, so the focus ring would animate from its pre-focus value -- the
|
|
500
|
+
initial `currentColor`, i.e. the control's own text color -- over 150ms.
|
|
501
|
+
On a filled variant that is a near-white ring for the first frames, which
|
|
502
|
+
is no focus indicator at all; it also makes every getComputedStyle read
|
|
503
|
+
taken right after a Tab press report the previous color, which is how
|
|
504
|
+
this was mis-reported as an inert --focus-ring-color token. Keep
|
|
505
|
+
outline-color out of every transition in this engine. */
|
|
506
|
+
@apply transition-[color,background-color,border-color,text-decoration-color];
|
|
478
507
|
@apply select-none;
|
|
479
508
|
|
|
480
509
|
/* SVG icons inside buttons */
|
|
@@ -591,10 +620,20 @@
|
|
|
591
620
|
* variant declares, so a single selector suffices and no variant can clobber
|
|
592
621
|
* it; it also survives forced-colors mode and overflow-hidden ancestors, and
|
|
593
622
|
* needs no knowledge of the backdrop color.
|
|
623
|
+
*
|
|
624
|
+
* Longhands, not the `outline` shorthand. The shorthand is invalid at
|
|
625
|
+
* computed-value time as a unit: if any one var() fails to resolve, all three
|
|
626
|
+
* longhands reset to their initial values and the ring vanishes entirely --
|
|
627
|
+
* and `outline-color: currentColor` looks plausible on a dark-text control,
|
|
628
|
+
* which is how a missing ring stays unreported. Longhands isolate a failure
|
|
629
|
+
* to the property that actually failed.
|
|
630
|
+
*
|
|
631
|
+
* The base rule must never transition outline-color -- see the note there.
|
|
594
632
|
*/
|
|
595
633
|
[data-component="button"]:where(:focus-visible) {
|
|
596
|
-
outline: var(--focus-ring-width, 3px)
|
|
597
|
-
|
|
634
|
+
outline-width: var(--focus-ring-width, 3px);
|
|
635
|
+
outline-style: var(--focus-ring-style, solid);
|
|
636
|
+
outline-color: var(--focus-ring-color, var(--ring));
|
|
598
637
|
outline-offset: var(--focus-ring-offset, 0px);
|
|
599
638
|
}
|
|
600
639
|
|
|
@@ -626,7 +665,11 @@
|
|
|
626
665
|
[data-form-part="error"] {
|
|
627
666
|
@apply text-sm;
|
|
628
667
|
font-weight: var(--label-weight, 500);
|
|
629
|
-
|
|
668
|
+
/* Error text sits on the page, not on a destructive fill, so it is the one
|
|
669
|
+
place --destructive-foreground is the wrong role. Which of the pair is
|
|
670
|
+
readable as text depends on the host palette's convention, so it routes
|
|
671
|
+
through its own token: see docs/theming.md. */
|
|
672
|
+
color: var(--destructive-text, var(--destructive-foreground));
|
|
630
673
|
}
|
|
631
674
|
|
|
632
675
|
/* Rails error wrapper compatibility */
|
|
@@ -638,9 +681,15 @@
|
|
|
638
681
|
:where(.field_with_errors) [data-component="textarea"],
|
|
639
682
|
:where(.field_with_errors) [data-component="select"] {
|
|
640
683
|
--focus-ring-color: color-mix(in oklch, var(--destructive) 20%, transparent);
|
|
641
|
-
border-color: var(--destructive);
|
|
642
|
-
|
|
643
|
-
|
|
684
|
+
border-color: var(--destructive-border, var(--destructive));
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
:where(.field_with_errors) [data-component="input"]:where(:focus-visible),
|
|
688
|
+
:where(.field_with_errors) [data-component="textarea"]:where(:focus-visible),
|
|
689
|
+
:where(.field_with_errors) [data-component="select"]:where(:focus-visible) {
|
|
690
|
+
outline-width: var(--focus-ring-width, 3px);
|
|
691
|
+
outline-style: var(--focus-ring-style, solid);
|
|
692
|
+
outline-color: var(--focus-ring-color, color-mix(in oklch, var(--destructive) 20%, transparent));
|
|
644
693
|
outline-offset: var(--focus-ring-offset, 0px);
|
|
645
694
|
}
|
|
646
695
|
|
|
@@ -77,8 +77,9 @@
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
[data-menu-button-part="trigger"]:where(:focus-visible) {
|
|
80
|
-
outline: var(--focus-ring-width, 3px)
|
|
81
|
-
|
|
80
|
+
outline-width: var(--focus-ring-width, 3px);
|
|
81
|
+
outline-style: var(--focus-ring-style, solid);
|
|
82
|
+
outline-color: var(--focus-ring-color, var(--sidebar-ring, var(--ring)));
|
|
82
83
|
outline-offset: var(--focus-ring-offset, 0px);
|
|
83
84
|
}
|
|
84
85
|
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
@apply text-sm;
|
|
59
59
|
border-radius: var(--pagination-radius, var(--control-radius, 0.375rem));
|
|
60
60
|
font-weight: var(--label-weight, 500);
|
|
61
|
-
@apply transition-
|
|
61
|
+
@apply transition-[color,background-color,border-color,text-decoration-color] duration-150;
|
|
62
62
|
|
|
63
63
|
/* Size */
|
|
64
64
|
@apply h-9 min-w-9 px-3;
|
|
@@ -136,8 +136,9 @@
|
|
|
136
136
|
[data-pagination-part="link"]:where(:focus-visible),
|
|
137
137
|
[data-pagination-part="previous"]:where(:focus-visible),
|
|
138
138
|
[data-pagination-part="next"]:where(:focus-visible) {
|
|
139
|
-
outline: var(--focus-ring-width, 3px)
|
|
140
|
-
|
|
139
|
+
outline-width: var(--focus-ring-width, 3px);
|
|
140
|
+
outline-style: var(--focus-ring-style, solid);
|
|
141
|
+
outline-color: var(--focus-ring-color, var(--ring));
|
|
141
142
|
outline-offset: var(--focus-ring-offset, 0px);
|
|
142
143
|
}
|
|
143
144
|
|