@ktortu/aaa 0.1.0-beta.0
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.
- package/LICENSE +21 -0
- package/README.md +151 -0
- package/button/button-tokens.css +152 -0
- package/button/button.css +319 -0
- package/card/card-tokens.css +49 -0
- package/card/card.css +200 -0
- package/cdk/styles/foundation.css +83 -0
- package/cdk/styles/tabs.css +276 -0
- package/dialog/dialog.css +350 -0
- package/fesm2022/ktortu-aaa-button.mjs +128 -0
- package/fesm2022/ktortu-aaa-button.mjs.map +1 -0
- package/fesm2022/ktortu-aaa-card.mjs +209 -0
- package/fesm2022/ktortu-aaa-card.mjs.map +1 -0
- package/fesm2022/ktortu-aaa-cdk.mjs +183 -0
- package/fesm2022/ktortu-aaa-cdk.mjs.map +1 -0
- package/fesm2022/ktortu-aaa-dialog.mjs +512 -0
- package/fesm2022/ktortu-aaa-dialog.mjs.map +1 -0
- package/fesm2022/ktortu-aaa-forms.mjs +3215 -0
- package/fesm2022/ktortu-aaa-forms.mjs.map +1 -0
- package/fesm2022/ktortu-aaa-menu.mjs +315 -0
- package/fesm2022/ktortu-aaa-menu.mjs.map +1 -0
- package/fesm2022/ktortu-aaa-tabs.mjs +79 -0
- package/fesm2022/ktortu-aaa-tabs.mjs.map +1 -0
- package/fesm2022/ktortu-aaa-tooltip.mjs +356 -0
- package/fesm2022/ktortu-aaa-tooltip.mjs.map +1 -0
- package/fesm2022/ktortu-aaa.mjs +17 -0
- package/fesm2022/ktortu-aaa.mjs.map +1 -0
- package/forms/checkbox/checkbox-group.css +55 -0
- package/forms/checkbox/checkbox.css +216 -0
- package/forms/chips/chip-list.css +70 -0
- package/forms/chips/chip.css +92 -0
- package/forms/chips/tokens.css +102 -0
- package/forms/field/field.css +87 -0
- package/forms/multi-select/multi-select.css +136 -0
- package/forms/radio/radio-group.css +55 -0
- package/forms/radio/radio.css +165 -0
- package/forms/styles/field-box.css +171 -0
- package/forms/styles/select-panel.css +464 -0
- package/forms/styles/tokens.css +67 -0
- package/forms/switch/switch.css +188 -0
- package/menu/menu-tokens.css +58 -0
- package/menu/menu.css +224 -0
- package/package.json +96 -0
- package/styles/button.css +6 -0
- package/styles/card.css +6 -0
- package/styles/dialog.css +6 -0
- package/styles/forms.css +13 -0
- package/styles/foundation.css +7 -0
- package/styles/menu.css +6 -0
- package/styles/styles.css +24 -0
- package/styles/tabs.css +5 -0
- package/styles/tooltip.css +5 -0
- package/themes/theme-ant.css +44 -0
- package/themes/theme-architecte.css +83 -0
- package/themes/theme-aurora.css +97 -0
- package/themes/theme-bootstrap.css +46 -0
- package/themes/theme-carbon.css +49 -0
- package/themes/theme-catppuccin.css +66 -0
- package/themes/theme-cyberpunk.css +211 -0
- package/themes/theme-fluent.css +45 -0
- package/themes/theme-material-you.css +74 -0
- package/themes/theme-material.css +48 -0
- package/themes/theme-primer.css +46 -0
- package/themes/theme-vegetal.css +78 -0
- package/tooltip/tooltip.css +129 -0
- package/types/ktortu-aaa-button.d.ts +70 -0
- package/types/ktortu-aaa-card.d.ts +143 -0
- package/types/ktortu-aaa-cdk.d.ts +110 -0
- package/types/ktortu-aaa-dialog.d.ts +286 -0
- package/types/ktortu-aaa-forms.d.ts +1574 -0
- package/types/ktortu-aaa-menu.d.ts +171 -0
- package/types/ktortu-aaa-tabs.d.ts +27 -0
- package/types/ktortu-aaa-tooltip.d.ts +90 -0
- package/types/ktortu-aaa.d.ts +8 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
@layer kt-aaa.components {
|
|
2
|
+
:host {
|
|
3
|
+
display: block;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.kt-checkbox-field {
|
|
7
|
+
display: flex;
|
|
8
|
+
flex-direction: column;
|
|
9
|
+
gap: var(--field-gap, 0.375rem);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/* Surface cliquable (label) : remplit tout le host, cible tactile confortable (AAA 2.5.5) sur
|
|
13
|
+
toute la rangée. Rembourrage par token (`--checkbox-padding`) pour bâtir une carte/option
|
|
14
|
+
cliquable sans franchir l'encapsulation ; rayon hérité du host. */
|
|
15
|
+
.kt-checkbox {
|
|
16
|
+
display: flex;
|
|
17
|
+
align-items: center;
|
|
18
|
+
gap: var(--field-control-gap, 0.5rem);
|
|
19
|
+
inline-size: 100%;
|
|
20
|
+
box-sizing: border-box;
|
|
21
|
+
min-block-size: var(--checkbox-target-size, 44px);
|
|
22
|
+
padding: var(--checkbox-padding, 0);
|
|
23
|
+
border-radius: inherit;
|
|
24
|
+
cursor: pointer;
|
|
25
|
+
user-select: none;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/* La case elle-même : input natif redessiné. Taille pilotée par UN token via `font-size`
|
|
29
|
+
(la box et la coche s'expriment en `em`, donc tout suit `--checkbox-size`). */
|
|
30
|
+
.kt-checkbox__input {
|
|
31
|
+
appearance: none;
|
|
32
|
+
-webkit-appearance: none;
|
|
33
|
+
margin: 0;
|
|
34
|
+
flex: none;
|
|
35
|
+
box-sizing: border-box;
|
|
36
|
+
display: grid;
|
|
37
|
+
place-content: center;
|
|
38
|
+
font-size: var(--checkbox-size, 1.25rem);
|
|
39
|
+
inline-size: 1em;
|
|
40
|
+
block-size: 1em;
|
|
41
|
+
border: var(--checkbox-border-width, 2px) solid
|
|
42
|
+
var(--checkbox-border-color, var(--field-border-color, var(--kt-outline, #c4c7c5)));
|
|
43
|
+
border-radius: var(--checkbox-radius, 0.25rem);
|
|
44
|
+
background-color: var(--checkbox-bg, var(--field-bg, var(--kt-surface, #ffffff)));
|
|
45
|
+
cursor: pointer;
|
|
46
|
+
/* Élévation/glow de repos : retombe sur le token de thème des champs (--field-shadow) pour que
|
|
47
|
+
la case « rayonne » comme les champs sous un thème (aurora, cyberpunk…). `none` par défaut. */
|
|
48
|
+
box-shadow: var(--checkbox-shadow, var(--field-shadow, none));
|
|
49
|
+
/* Token de BASCULE (cf. --switch-transition) : transitionne aussi box-shadow pour animer le glow. */
|
|
50
|
+
transition: var(
|
|
51
|
+
--checkbox-transition,
|
|
52
|
+
background-color 0.15s cubic-bezier(0.4, 0, 0.2, 1),
|
|
53
|
+
border-color 0.15s cubic-bezier(0.4, 0, 0.2, 1),
|
|
54
|
+
box-shadow 0.2s ease
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/* La coche : pseudo-élément masqué par clip-path, mis à l'échelle 0 → 1.
|
|
59
|
+
`box-shadow: inset` remplit la forme de la couleur de coche (technique cross-browser fiable). */
|
|
60
|
+
.kt-checkbox__input::before {
|
|
61
|
+
content: '';
|
|
62
|
+
inline-size: 0.65em;
|
|
63
|
+
block-size: 0.65em;
|
|
64
|
+
transform: scale(0);
|
|
65
|
+
transform-origin: center;
|
|
66
|
+
box-shadow: inset 1em 1em var(--checkbox-mark-color, #ffffff);
|
|
67
|
+
clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
|
|
68
|
+
/* Décochage : repli doux vers scale(0). */
|
|
69
|
+
transition: var(--checkbox-mark-transition, transform 0.12s ease-in);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/* Cochage : « pop » avec rebond via @keyframes — fiable car déclenché à l'ENTRÉE dans l'état
|
|
73
|
+
`:checked` (une transition, elle, exige un diff de valeur entre deux recalculs, que la
|
|
74
|
+
ré-écriture de `checked` par Angular écrase → elle ne démarrait pas). */
|
|
75
|
+
.kt-checkbox__input:checked::before,
|
|
76
|
+
.kt-checkbox__input:indeterminate::before {
|
|
77
|
+
transform: scale(1);
|
|
78
|
+
animation: var(--checkbox-mark-animation, kt-checkbox-pop 0.22s cubic-bezier(0.34, 1.56, 0.64, 1));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/* État indéterminé : une barre horizontale au lieu de la coche. */
|
|
82
|
+
.kt-checkbox__input:indeterminate::before {
|
|
83
|
+
clip-path: inset(42% 12%);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
@keyframes kt-checkbox-pop {
|
|
87
|
+
from {
|
|
88
|
+
transform: scale(0);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/* États COCHÉ / INDÉTERMINÉ : remplissage plein + HALO coloré dérivé du primaire du thème
|
|
93
|
+
(« glow » visible dans tous les thèmes ; apparaît en fondu via la transition box-shadow).
|
|
94
|
+
Surchargeable/désactivable par thème via --checkbox-shadow-checked. */
|
|
95
|
+
.kt-checkbox__input:checked,
|
|
96
|
+
.kt-checkbox__input:indeterminate {
|
|
97
|
+
background-color: var(--checkbox-bg-checked, var(--kt-primary, #0842a0));
|
|
98
|
+
border-color: var(--checkbox-border-color-checked, var(--checkbox-bg-checked, var(--kt-primary, #0842a0)));
|
|
99
|
+
box-shadow: var(
|
|
100
|
+
--checkbox-shadow-checked,
|
|
101
|
+
0 0 8px color-mix(in srgb, var(--checkbox-bg-checked, var(--kt-primary, #0842a0)) 50%, transparent)
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/* HOVER : bordure + glow de survol du thème (--field-shadow-hover). */
|
|
106
|
+
.kt-checkbox__input:hover:not(:disabled) {
|
|
107
|
+
border-color: var(
|
|
108
|
+
--checkbox-border-color-hover,
|
|
109
|
+
var(--field-border-color-hover, var(--kt-outline-strong, #5f6368))
|
|
110
|
+
);
|
|
111
|
+
box-shadow: var(
|
|
112
|
+
--checkbox-shadow-hover,
|
|
113
|
+
var(--field-shadow-hover, var(--checkbox-shadow, var(--field-shadow, none)))
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.kt-checkbox__input:checked:hover:not(:disabled),
|
|
118
|
+
.kt-checkbox__input:indeterminate:hover:not(:disabled) {
|
|
119
|
+
background-color: var(--checkbox-bg-checked-hover, color-mix(in srgb, var(--kt-primary, #0842a0) 90%, black));
|
|
120
|
+
border-color: var(--checkbox-bg-checked-hover, color-mix(in srgb, var(--kt-primary, #0842a0) 90%, black));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/* FOCUS : anneau via `outline` (comme les champs → pas de conflit avec le glow box-shadow), +
|
|
124
|
+
glow de focus du thème (--field-shadow-focus). L'outline reste visible quel que soit le survol. */
|
|
125
|
+
.kt-checkbox__input:focus-visible {
|
|
126
|
+
outline: var(--kt-focus-ring-width, 2px) solid var(--kt-focus-ring-color, #0842a0);
|
|
127
|
+
outline-offset: var(--checkbox-focus-ring-offset, 2px);
|
|
128
|
+
box-shadow: var(--checkbox-shadow-focus, var(--field-shadow-focus, var(--field-shadow, none)));
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/* DÉSACTIVÉ */
|
|
132
|
+
.kt-checkbox__input:disabled {
|
|
133
|
+
cursor: not-allowed;
|
|
134
|
+
opacity: 0.5;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.kt-checkbox-field--disabled .kt-checkbox {
|
|
138
|
+
cursor: not-allowed;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/* INVALIDE */
|
|
142
|
+
.kt-checkbox-field--invalid .kt-checkbox__input:not(:checked):not(:indeterminate) {
|
|
143
|
+
border-color: var(--field-error-color, var(--kt-danger, #8c1d18));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/* Libellé */
|
|
147
|
+
.kt-checkbox__label {
|
|
148
|
+
font-size: var(--field-label-font-size, 0.875rem);
|
|
149
|
+
font-weight: var(--field-label-weight, 500);
|
|
150
|
+
color: var(--field-label-color, inherit);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.kt-checkbox-field--disabled .kt-checkbox__label {
|
|
154
|
+
color: var(--field-hint-color, #5f6368);
|
|
155
|
+
opacity: 0.6;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.kt-checkbox__required {
|
|
159
|
+
margin-inline-start: 0.125rem;
|
|
160
|
+
color: var(--field-required-color, var(--kt-danger, #8c1d18));
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/* Hint & erreur */
|
|
164
|
+
.kt-checkbox-hint {
|
|
165
|
+
margin: 0;
|
|
166
|
+
font-size: var(--field-hint-font-size, 0.8125rem);
|
|
167
|
+
color: var(--field-hint-color, #474747);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.kt-checkbox-error {
|
|
171
|
+
margin: 0;
|
|
172
|
+
display: flex;
|
|
173
|
+
flex-direction: column;
|
|
174
|
+
font-size: var(--field-hint-font-size, 0.8125rem);
|
|
175
|
+
color: var(--field-error-color, #8c1d18);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.kt-checkbox-error-message {
|
|
179
|
+
animation: var(--field-error-animation, none);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/* Mode contraste élevé (Windows) : `appearance:none` casse le rendu système → on le restaure
|
|
183
|
+
avec les couleurs système (obligatoire RGAA dès qu'on redessine un contrôle natif). */
|
|
184
|
+
@media (forced-colors: active) {
|
|
185
|
+
.kt-checkbox__input {
|
|
186
|
+
border-color: CanvasText;
|
|
187
|
+
background-color: Canvas;
|
|
188
|
+
}
|
|
189
|
+
.kt-checkbox__input:checked,
|
|
190
|
+
.kt-checkbox__input:indeterminate {
|
|
191
|
+
background-color: Highlight;
|
|
192
|
+
border-color: Highlight;
|
|
193
|
+
}
|
|
194
|
+
.kt-checkbox__input::before {
|
|
195
|
+
box-shadow: inset 1em 1em HighlightText;
|
|
196
|
+
}
|
|
197
|
+
.kt-checkbox__input:disabled {
|
|
198
|
+
border-color: GrayText;
|
|
199
|
+
}
|
|
200
|
+
.kt-checkbox__input:focus-visible {
|
|
201
|
+
outline: 2px solid CanvasText;
|
|
202
|
+
outline-offset: 2px;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
@media (prefers-reduced-motion: reduce) {
|
|
207
|
+
.kt-checkbox__input,
|
|
208
|
+
.kt-checkbox__input::before {
|
|
209
|
+
transition: none;
|
|
210
|
+
animation: none;
|
|
211
|
+
}
|
|
212
|
+
.kt-checkbox-error-message {
|
|
213
|
+
animation: none;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
@layer kt-aaa.components {
|
|
2
|
+
:host {
|
|
3
|
+
display: block;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.kt-chip-list {
|
|
7
|
+
display: flex;
|
|
8
|
+
flex-wrap: wrap;
|
|
9
|
+
gap: 0.5rem;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/* Porte le role="list" sans créer de contexte de mise en page : chips et bouton « +N more »
|
|
13
|
+
restent dans la même rangée flex de .kt-chip-list. */
|
|
14
|
+
.kt-chip-list__items {
|
|
15
|
+
display: contents;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/* Wrapper transparent : le rôle listitem est porté ici, le rendu vient du template custom
|
|
19
|
+
ou du bouton « +N more ». */
|
|
20
|
+
.kt-chip-list__item {
|
|
21
|
+
display: contents;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/* Bouton de repli/dépli : même habillage pilule que kt-chip (dupliqué : c'est un bouton,
|
|
25
|
+
pas un chip — il ne partage pas l'hôte du composant Chip). */
|
|
26
|
+
.kt-chip-list__more {
|
|
27
|
+
display: inline-flex;
|
|
28
|
+
align-items: center;
|
|
29
|
+
gap: var(--chip-gap);
|
|
30
|
+
padding: var(--chip-padding-y) var(--chip-padding-x);
|
|
31
|
+
border-radius: var(--chip-radius);
|
|
32
|
+
background: var(--chip-bg);
|
|
33
|
+
border: 1px solid var(--chip-border);
|
|
34
|
+
box-shadow: var(--chip-shadow, none);
|
|
35
|
+
font: inherit;
|
|
36
|
+
font-size: var(--chip-font-size);
|
|
37
|
+
color: var(--chip-color);
|
|
38
|
+
cursor: pointer;
|
|
39
|
+
min-block-size: 24px;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.kt-chip-list__more:hover {
|
|
43
|
+
background: var(--chip-bg-hover);
|
|
44
|
+
box-shadow: var(--chip-shadow-hover, var(--chip-shadow, none));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.kt-chip-list__more:focus-visible {
|
|
48
|
+
outline: 2px solid var(--chip-focus-ring);
|
|
49
|
+
outline-offset: 1px;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/* Texte réservé aux lecteurs d'écran (live region des retraits). */
|
|
53
|
+
.kt-chip-list__status {
|
|
54
|
+
position: absolute;
|
|
55
|
+
inline-size: 1px;
|
|
56
|
+
block-size: 1px;
|
|
57
|
+
padding: 0;
|
|
58
|
+
margin: -1px;
|
|
59
|
+
overflow: hidden;
|
|
60
|
+
clip-path: inset(50%);
|
|
61
|
+
white-space: nowrap;
|
|
62
|
+
border: 0;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
@media (pointer: coarse) {
|
|
66
|
+
.kt-chip-list__more {
|
|
67
|
+
min-block-size: 32px;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
@layer kt-aaa.components {
|
|
2
|
+
/* Pilule. Tokens centralisés dans forms/chips/tokens.css */
|
|
3
|
+
:host {
|
|
4
|
+
display: inline-flex;
|
|
5
|
+
align-items: center;
|
|
6
|
+
gap: var(--chip-gap);
|
|
7
|
+
padding: var(--chip-padding-y) var(--chip-padding-x);
|
|
8
|
+
border-radius: var(--chip-radius);
|
|
9
|
+
background: var(--chip-bg);
|
|
10
|
+
border: 1px solid var(--chip-border);
|
|
11
|
+
box-shadow: var(--chip-shadow, none);
|
|
12
|
+
font-size: var(--chip-font-size);
|
|
13
|
+
color: var(--chip-color);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.kt-chip__label {
|
|
17
|
+
max-inline-size: 12rem;
|
|
18
|
+
overflow: hidden;
|
|
19
|
+
text-overflow: ellipsis;
|
|
20
|
+
white-space: nowrap;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/* 24px visibles (WCAG 2.5.8 AA) ; cible réelle 44px via ::after transparent (AAA),
|
|
24
|
+
même technique que les boutons icon-only — le chip reste fin visuellement. */
|
|
25
|
+
.kt-chip__remove {
|
|
26
|
+
position: relative;
|
|
27
|
+
display: inline-flex;
|
|
28
|
+
align-items: center;
|
|
29
|
+
justify-content: center;
|
|
30
|
+
inline-size: 24px;
|
|
31
|
+
block-size: 24px;
|
|
32
|
+
padding: 0;
|
|
33
|
+
border: 0;
|
|
34
|
+
border-radius: 50%;
|
|
35
|
+
background: transparent;
|
|
36
|
+
color: var(--chip-remove-color);
|
|
37
|
+
cursor: pointer;
|
|
38
|
+
font-size: 0.75rem;
|
|
39
|
+
/* Token de BASCULE (non déclaré => hover actuel). Contrat : forms/chips/tokens.css. */
|
|
40
|
+
transition: var(--chip-remove-transition, background-color 0.15s ease);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.kt-chip__remove::after {
|
|
44
|
+
content: '';
|
|
45
|
+
position: absolute;
|
|
46
|
+
inset: -10px;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.kt-chip__remove:hover {
|
|
50
|
+
background-color: var(--chip-remove-bg-hover);
|
|
51
|
+
color: var(--chip-remove-color-hover);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
:host(:hover) {
|
|
55
|
+
box-shadow: var(--chip-shadow-hover, var(--chip-shadow, none));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.kt-chip__remove:focus-visible {
|
|
59
|
+
outline: 2px solid var(--chip-focus-ring);
|
|
60
|
+
outline-offset: 1px;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.kt-chip__remove:disabled {
|
|
64
|
+
cursor: not-allowed;
|
|
65
|
+
opacity: 0.5;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.kt-chip__close-icon {
|
|
69
|
+
font-family: 'Material Symbols Outlined';
|
|
70
|
+
font-feature-settings: 'liga';
|
|
71
|
+
font-size: 1rem;
|
|
72
|
+
line-height: 1;
|
|
73
|
+
-webkit-font-smoothing: antialiased;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/* Tactile : à peine plus généreux (32px), la cible 44px vient du ::after invisible. */
|
|
77
|
+
@media (pointer: coarse) {
|
|
78
|
+
:host {
|
|
79
|
+
min-block-size: 32px;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.kt-chip__remove {
|
|
83
|
+
inline-size: 28px;
|
|
84
|
+
block-size: 28px;
|
|
85
|
+
font-size: 0.875rem;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.kt-chip__remove::after {
|
|
89
|
+
inset: -8px;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/* Design tokens du composant Chip — contrat public de theming.
|
|
2
|
+
Les couleurs et géométries dérivent du socle (--kt-*) et des champs (--field-*). */
|
|
3
|
+
@layer kt-aaa.tokens {
|
|
4
|
+
:root {
|
|
5
|
+
/* Géométrie */
|
|
6
|
+
--chip-radius: 50px;
|
|
7
|
+
--chip-padding-y: 0.25rem;
|
|
8
|
+
--chip-padding-x: 0.625rem;
|
|
9
|
+
--chip-gap: 0.25rem;
|
|
10
|
+
--chip-font-size: 0.875rem;
|
|
11
|
+
|
|
12
|
+
/* Couleurs (dérivées de ds-primary) */
|
|
13
|
+
--chip-bg: color-mix(in srgb, var(--kt-primary) 10%, transparent);
|
|
14
|
+
--chip-bg-hover: color-mix(in srgb, var(--kt-primary) 18%, transparent);
|
|
15
|
+
--chip-border: color-mix(in srgb, var(--kt-primary) 20%, transparent);
|
|
16
|
+
|
|
17
|
+
/* Texte et icônes */
|
|
18
|
+
--chip-color: var(--field-color);
|
|
19
|
+
--chip-remove-color: var(--field-icon-color);
|
|
20
|
+
--chip-remove-color-hover: var(--kt-danger); /* rouge au survol du bouton × */
|
|
21
|
+
--chip-remove-bg-hover: color-mix(in srgb, currentColor 15%, transparent);
|
|
22
|
+
|
|
23
|
+
/* Animations d'entrée/sortie : keyframes jouées sur les PSEUDO-ÉLÉMENTS View Transition
|
|
24
|
+
(cf. règles ::view-transition-* plus bas), PAS sur les chips eux-mêmes — la suppression
|
|
25
|
+
DOM reste synchrone (indispensable : un retrait différé casse la capture de l'état
|
|
26
|
+
« après » et tue la transition en plein vol). Les keyframes d'un thème ne doivent animer
|
|
27
|
+
que transform / opacity / filter : la géométrie (largeur, position) est déjà morphée par
|
|
28
|
+
l'animation de groupe. Défaut : fondu équivalent à celui du navigateur. */
|
|
29
|
+
--chip-enter-animation: chip-vt-fade-in 0.25s ease both;
|
|
30
|
+
--chip-leave-animation: chip-vt-fade-out 0.25s ease both;
|
|
31
|
+
|
|
32
|
+
/* Focus */
|
|
33
|
+
--chip-focus-ring: var(--field-border-color-focus);
|
|
34
|
+
|
|
35
|
+
/* Token de BASCULE (NON déclaré exprès, cf. button-tokens.css) :
|
|
36
|
+
--chip-remove-transition — transition du bouton « retirer » au survol
|
|
37
|
+
(défaut : background-color 0.15s ease, posé dans chip.css). */
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/* ============ Intégration View Transition (API document.startViewTransition) ============
|
|
41
|
+
Chaque chip porte un view-transition-name unique + la classe .chip-transition (cf. Chip).
|
|
42
|
+
Ces règles ciblent les pseudo-éléments de la transition, rattachés à la racine du document :
|
|
43
|
+
elles doivent vivre dans une feuille GLOBALE (les styles de composant scopés ne les
|
|
44
|
+
atteignent pas). Les tokens --chip-*-animation y sont résolus par héritage depuis <html>,
|
|
45
|
+
donc surchargeables par thème ([data-theme='…']). */
|
|
46
|
+
|
|
47
|
+
@keyframes chip-vt-fade-in {
|
|
48
|
+
from {
|
|
49
|
+
opacity: 0;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
@keyframes chip-vt-fade-out {
|
|
54
|
+
to {
|
|
55
|
+
opacity: 0;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/* Chips à template custom (ktChipItem / chipTemplate) : la lib ne rend pas kt-chip,
|
|
60
|
+
c'est l'élément RACINE du template consommateur qui reçoit un nom automatique → il
|
|
61
|
+
participe aux mêmes animations (entrée/sortie/morph) que les chips standards.
|
|
62
|
+
Scopé à [data-vt-active] (posé par ChipList UNIQUEMENT pendant SA transition) : un nom
|
|
63
|
+
permanent ferait participer tous les chips de la page à chaque View Transition —
|
|
64
|
+
glissement individuel au moindre reflow (même principe que le gating de kt-chip).
|
|
65
|
+
Surcharge consommateur : redéclarer view-transition-name sur son élément —
|
|
66
|
+
`none` = opt-out, un nom explicite = chorégraphie maison ; ou une autre
|
|
67
|
+
view-transition-class pour des keyframes dédiées. Le bouton « +N » (dans le même
|
|
68
|
+
wrapper) n'est pas concerné : son nom est posé en style inline (prioritaire).
|
|
69
|
+
Navigateurs sans `view-transition-name: auto` : dégradation = pas d'animation
|
|
70
|
+
par chip (comportement antérieur), le reste de la transition est inchangé. */
|
|
71
|
+
[data-vt-active] .kt-chip-list__item > * {
|
|
72
|
+
/* Double déclaration volontaire : `match-element` (Chrome 140+) prime quand il est
|
|
73
|
+
compris, sinon retombée sur `auto` (spec antérieure) ; aucun des deux compris =
|
|
74
|
+
déclaration ignorée (dégradation propre). */
|
|
75
|
+
view-transition-name: auto;
|
|
76
|
+
view-transition-name: match-element;
|
|
77
|
+
view-transition-class: chip-transition;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/* Chip qui APPARAÎT (présent uniquement dans l'état « après ») : animation d'entrée. */
|
|
81
|
+
::view-transition-new(.chip-transition):only-child {
|
|
82
|
+
animation: var(--chip-enter-animation);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/* Chip qui DISPARAÎT (présent uniquement dans l'état « avant ») : animation de sortie. */
|
|
86
|
+
::view-transition-old(.chip-transition):only-child {
|
|
87
|
+
animation: var(--chip-leave-animation);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/* Chip qui PERSISTE (déplacement) : pas de cross-fade — l'ancienne image est masquée, la
|
|
91
|
+
nouvelle (rendu live) suit le morph de géométrie du groupe. Évite le scintillement
|
|
92
|
+
plus-lighter quand le contenu is identique mais que les ombres du thème bougent. */
|
|
93
|
+
::view-transition-old(.chip-transition):not(:only-child) {
|
|
94
|
+
animation: none;
|
|
95
|
+
opacity: 0;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
::view-transition-new(.chip-transition):not(:only-child) {
|
|
99
|
+
animation: none;
|
|
100
|
+
opacity: 1;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
@layer kt-aaa.components {
|
|
2
|
+
/* Field = chrome seul (empilement label / contrôle / hint / erreur).
|
|
3
|
+
Le visuel du contrôle (bordure, focus) appartient au composant de champ, pas ici. */
|
|
4
|
+
:host {
|
|
5
|
+
display: flex;
|
|
6
|
+
flex-direction: column;
|
|
7
|
+
gap: var(--field-gap, 0.375rem);
|
|
8
|
+
font-size: var(--field-font-size, 1rem);
|
|
9
|
+
color: var(--field-color, inherit);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.kt-field__header {
|
|
13
|
+
display: flex;
|
|
14
|
+
align-items: center;
|
|
15
|
+
gap: 0.375rem;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.kt-field__label {
|
|
19
|
+
font-size: var(--field-label-font-size, 0.875rem);
|
|
20
|
+
font-weight: var(--field-label-weight, 500);
|
|
21
|
+
text-transform: var(--field-label-transform, none);
|
|
22
|
+
letter-spacing: var(--field-label-letter-spacing, normal);
|
|
23
|
+
color: var(--field-label-color, inherit);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.kt-field__help {
|
|
27
|
+
display: inline-flex;
|
|
28
|
+
align-items: center;
|
|
29
|
+
justify-content: center;
|
|
30
|
+
padding: 0;
|
|
31
|
+
border: 0;
|
|
32
|
+
background: transparent;
|
|
33
|
+
color: var(--field-icon-color, #474747);
|
|
34
|
+
cursor: pointer;
|
|
35
|
+
position: relative; /* requis pour le positionnement de ::after */
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/* Zone de clic étendue à 44x44px (AAA 2.5.5) via un pseudo-élément transparent */
|
|
39
|
+
.kt-field__help::after {
|
|
40
|
+
content: '';
|
|
41
|
+
position: absolute;
|
|
42
|
+
inset: -12px;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.kt-field__help-icon {
|
|
46
|
+
font-family: 'Material Symbols Outlined';
|
|
47
|
+
font-size: 1.25rem;
|
|
48
|
+
line-height: 1;
|
|
49
|
+
font-feature-settings: 'liga';
|
|
50
|
+
-webkit-font-smoothing: antialiased;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.kt-field__required {
|
|
54
|
+
margin-inline-start: 0.125rem;
|
|
55
|
+
color: var(--field-required-color, #8c1d18);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.kt-field__hint {
|
|
59
|
+
margin: 0;
|
|
60
|
+
font-size: var(--field-hint-font-size, 0.8125rem);
|
|
61
|
+
color: var(--field-hint-color, #474747);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/* Pas de hauteur réservée quand vide (contre-pied du subscript fantôme de Material) ;
|
|
65
|
+
l'élément reste dans le DOM pour rester une région live. */
|
|
66
|
+
.kt-field__error {
|
|
67
|
+
margin: 0;
|
|
68
|
+
display: flex;
|
|
69
|
+
flex-direction: column;
|
|
70
|
+
font-size: var(--field-hint-font-size, 0.8125rem);
|
|
71
|
+
color: var(--field-error-color, #8c1d18);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/* Apparition d'un message d'erreur : token de BASCULE (non déclaré => aucune animation).
|
|
75
|
+
Sur le MESSAGE (inséré à l'invalidation) — PAS sur le conteneur, qui vit en permanence
|
|
76
|
+
(région live). Keyframes de thème : transform/opacity/filter seulement (les spans sont
|
|
77
|
+
des flex items, donc transformables). Même token consommé par kt-switch (switch.css). */
|
|
78
|
+
.kt-field__error-message {
|
|
79
|
+
animation: var(--field-error-animation, none);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
@media (prefers-reduced-motion: reduce) {
|
|
83
|
+
.kt-field__error-message {
|
|
84
|
+
animation: none;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
@layer kt-aaa.components {
|
|
2
|
+
/* Styles SPÉCIFIQUES au multi-select. Tout le panneau commun (trigger, popup, listbox, options,
|
|
3
|
+
filtre, bottom-sheet mobile) vient de ../styles/select-panel.css, chargé en premier via
|
|
4
|
+
styleUrls — ici uniquement : croix « tout effacer », barre d'actions, compteur, checkboxes
|
|
5
|
+
d'option et chips révocables. */
|
|
6
|
+
|
|
7
|
+
/* Réserve la place de la croix (sibling absolu) : sur le TEXTE, pas sur le padding du
|
|
8
|
+
trigger — la flèche reste collée au bord, la croix se glisse entre texte et flèche. */
|
|
9
|
+
.kt-select__trigger--clearable .kt-select__value {
|
|
10
|
+
margin-inline-end: 2rem;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/* Tout effacer : entre le texte et la flèche, disposition standard « texte … ✕ ▾ ».
|
|
14
|
+
24px visibles ; cible étendue à 44px via ::after transparent (AAA). */
|
|
15
|
+
.kt-select__clear {
|
|
16
|
+
position: absolute;
|
|
17
|
+
inset-block-start: 50%;
|
|
18
|
+
inset-inline-end: 2.5rem;
|
|
19
|
+
translate: 0 -50%;
|
|
20
|
+
display: inline-flex;
|
|
21
|
+
align-items: center;
|
|
22
|
+
justify-content: center;
|
|
23
|
+
inline-size: 24px;
|
|
24
|
+
block-size: 24px;
|
|
25
|
+
padding: 0;
|
|
26
|
+
border: 0;
|
|
27
|
+
border-radius: 50%;
|
|
28
|
+
background: transparent;
|
|
29
|
+
color: var(--field-icon-color, #5f6368);
|
|
30
|
+
cursor: pointer;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.kt-select__clear::after {
|
|
34
|
+
content: '';
|
|
35
|
+
position: absolute;
|
|
36
|
+
inset: -10px;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.kt-select__clear:hover {
|
|
40
|
+
background-color: color-mix(in srgb, currentColor 12%, transparent);
|
|
41
|
+
color: var(--field-color, inherit);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.kt-select__clear:focus-visible {
|
|
45
|
+
outline: 2px solid var(--field-border-color-focus, #0b57d0);
|
|
46
|
+
outline-offset: 1px;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.kt-select__clear-icon {
|
|
50
|
+
font-family: 'Material Symbols Outlined';
|
|
51
|
+
font-feature-settings: 'liga';
|
|
52
|
+
font-size: 1.25rem;
|
|
53
|
+
line-height: 1;
|
|
54
|
+
-webkit-font-smoothing: antialiased;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/* Barre d'actions de masse (Tout sélectionner / Tout effacer) en tête de panneau. */
|
|
58
|
+
.kt-select__actions {
|
|
59
|
+
display: flex;
|
|
60
|
+
gap: 0.25rem;
|
|
61
|
+
flex: none;
|
|
62
|
+
padding: 0.25rem 0.5rem;
|
|
63
|
+
border-block-end: 1px solid var(--field-border-color, #c4c7c5);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.kt-select__action {
|
|
67
|
+
flex: 1;
|
|
68
|
+
min-block-size: 44px;
|
|
69
|
+
padding: 0.375rem 0.625rem;
|
|
70
|
+
border: 0;
|
|
71
|
+
border-radius: calc(var(--field-radius, 8px) * 0.75);
|
|
72
|
+
background: transparent;
|
|
73
|
+
color: var(--kt-primary, #0b57d0);
|
|
74
|
+
font: inherit;
|
|
75
|
+
font-weight: 500;
|
|
76
|
+
cursor: pointer;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.kt-select__action:hover {
|
|
80
|
+
background: var(--select-option-hover-bg, color-mix(in srgb, currentColor 8%, transparent));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.kt-select__action:focus-visible {
|
|
84
|
+
outline: 2px solid var(--field-border-color-focus, #0b57d0);
|
|
85
|
+
outline-offset: -2px;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/* Compteur de sélection visible (info SR portée par la live region → aria-hidden). */
|
|
89
|
+
.kt-select__count {
|
|
90
|
+
flex: none;
|
|
91
|
+
padding: 0.25rem 0.75rem;
|
|
92
|
+
font-size: 0.8125rem;
|
|
93
|
+
color: var(--field-hint-color, #5f6368);
|
|
94
|
+
border-block-end: 1px solid var(--field-border-color, #c4c7c5);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/* Checkbox visuelle dans la liste */
|
|
98
|
+
.kt-select__checkbox {
|
|
99
|
+
display: inline-flex;
|
|
100
|
+
align-items: center;
|
|
101
|
+
justify-content: center;
|
|
102
|
+
flex: none;
|
|
103
|
+
margin-inline-end: 0.25rem;
|
|
104
|
+
user-select: none;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.kt-select__checkbox-icon {
|
|
108
|
+
font-family: 'Material Symbols Outlined';
|
|
109
|
+
font-feature-settings: 'liga';
|
|
110
|
+
font-size: 1.25rem;
|
|
111
|
+
line-height: 1;
|
|
112
|
+
color: var(--field-icon-color, #5f6368);
|
|
113
|
+
-webkit-font-smoothing: antialiased;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.kt-select__option[aria-selected='true'] .kt-select__checkbox-icon {
|
|
117
|
+
color: var(--kt-primary, #0b57d0);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.kt-select__option--multiple {
|
|
121
|
+
display: flex;
|
|
122
|
+
align-items: center;
|
|
123
|
+
gap: 0.5rem;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.kt-select__option-content {
|
|
127
|
+
flex: 1;
|
|
128
|
+
min-inline-size: 0;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/* Chips délégués au composant kt-chip-list (hôte toujours rendu : la marge ne
|
|
132
|
+
s'applique que lorsqu'il y a des chips, via l'attribut data-empty). */
|
|
133
|
+
kt-chip-list:not([data-empty]) {
|
|
134
|
+
margin-block-start: 0.5rem;
|
|
135
|
+
}
|
|
136
|
+
}
|