@hgdc/ui 0.2.0 → 0.2.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.
- package/README.md +4 -0
- package/dist/badge.js +3 -3
- package/dist/button.js +6 -5
- package/dist/segmented-control.js +1 -3
- package/dist/sonner.js +2 -2
- package/dist/spinner.d.ts +15 -4
- package/dist/spinner.js +7 -6
- package/package.json +1 -1
- package/styles.css +29 -23
package/README.md
CHANGED
|
@@ -41,7 +41,11 @@ Un sous-chemin par composant, sans fichier d'index : `import { Badge } from "@hg
|
|
|
41
41
|
- Chrome monochrome, un seul accent violet : focus, lien, élément actif, un geste principal par écran.
|
|
42
42
|
- Profondeur par la teinte : ni ombre ni bordure dans le flux, ombre réservée à ce qui flotte.
|
|
43
43
|
- Boutons en pilule, champs et menus en `rounded-2xl`, cartes en `rounded-3xl`.
|
|
44
|
+
- Au survol, seule la couleur change : ni ombre, ni zoom. Le rebond est réservé à l'appui.
|
|
45
|
+
- Couleurs de statut : la couleur vive pour les pastilles, son encre (`text-status-active-ink`, `text-info-ink`…) pour le texte. L'information est bleue, le violet reste l'accent.
|
|
46
|
+
- Chargement : l'anneau de la DA, `<Spinner />` dans un bouton, `<Spinner size={8} tone="accent" />` seul sur une page.
|
|
44
47
|
- Un `Button` rendu en lien (`render={<Link />}`) reste un lien pour les lecteurs d'écran.
|
|
48
|
+
- Référence de l'interface : Host. Couleur d'un client : `--brand-accent`.
|
|
45
49
|
|
|
46
50
|
Mode d'emploi complet : [Adopter le kit](https://github.com/hugogebel-boop/hgdc-kit/blob/main/docs/guides/adopter-le-kit.md#7-interface). Décisions : [ADR 0005](https://github.com/hugogebel-boop/hgdc-kit/blob/main/docs/adr/0005-ui-css-brute-et-sous-chemins.md).
|
|
47
51
|
|
package/dist/badge.js
CHANGED
|
@@ -5,9 +5,9 @@ const badgeVariants = cva("inline-flex w-fit items-center gap-1 rounded-full px-
|
|
|
5
5
|
variants: {
|
|
6
6
|
tone: {
|
|
7
7
|
neutral: "bg-muted text-muted-foreground",
|
|
8
|
-
success: "bg-status-active/15 text-status-active",
|
|
9
|
-
warning: "bg-status-paused/15 text-status-paused",
|
|
10
|
-
info: "bg-
|
|
8
|
+
success: "bg-status-active/15 text-status-active-ink",
|
|
9
|
+
warning: "bg-status-paused/15 text-status-paused-ink",
|
|
10
|
+
info: "bg-info/12 text-info-ink",
|
|
11
11
|
destructive: "bg-destructive/10 text-destructive",
|
|
12
12
|
},
|
|
13
13
|
},
|
package/dist/button.js
CHANGED
|
@@ -22,17 +22,18 @@ const buttonVariants = cva(
|
|
|
22
22
|
// regarde à cet instant précis ; la relâche revient en 300 ms avec un petit
|
|
23
23
|
// rebond (`--ease-bounce`, révision du 2026-09-24).
|
|
24
24
|
//
|
|
25
|
-
// Au survol
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
//
|
|
25
|
+
// Au survol, seule la couleur change : ni ombre, ni échelle. Le zoom de
|
|
26
|
+
// 1.02 faisait clignoter le libellé (le texte passe sur un calque GPU au
|
|
27
|
+
// lissage différent pendant l'animation, puis revient), et la couleur, qui
|
|
28
|
+
// suivait la courbe à rebond, dépassait sa cible : un éclair sur `ghost`.
|
|
29
|
+
// La couleur suit donc `--ease-out` en 150 ms ; le rebond reste à l'échelle.
|
|
29
30
|
//
|
|
30
31
|
// Ni bordure transparente ni `bg-clip-padding` (retirés le 2026-09-27) : le
|
|
31
32
|
// fond s'arrêtait un pixel avant le bord, et dans une feuille du bas posée à
|
|
32
33
|
// une coordonnée fractionnaire (hauteur en `svh`, glissement), iOS laissait
|
|
33
34
|
// voir ce pixel comme un filet sombre en haut de la pilule, en thème sombre.
|
|
34
35
|
// Le focus et l'erreur passent par l'anneau seul.
|
|
35
|
-
"group/button touch-target inline-flex shrink-0 items-center justify-center rounded-full text-sm font-medium whitespace-nowrap [transition-
|
|
36
|
+
"group/button touch-target inline-flex shrink-0 items-center justify-center rounded-full text-sm font-medium whitespace-nowrap [transition:color_var(--duration-fast)_var(--ease-out),background-color_var(--duration-fast)_var(--ease-out),box-shadow_var(--duration-fast)_var(--ease-out),opacity_var(--duration-fast)_var(--ease-out),scale_var(--duration-slow)_var(--ease-bounce)] outline-none select-none focus-visible:ring-3 focus-visible:ring-ring/50 active:scale-[0.96] active:duration-(--duration-instant) active:ease-out disabled:pointer-events-none disabled:opacity-50 aria-invalid:ring-3 aria-invalid:ring-destructive/40 data-[pressed]:scale-[0.96] data-[pressed]:duration-(--duration-instant) data-[pressed]:ease-out dark:aria-invalid:ring-destructive/50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", {
|
|
36
37
|
variants: {
|
|
37
38
|
variant: {
|
|
38
39
|
default: "bg-primary text-primary-foreground hover:bg-primary/85",
|
|
@@ -13,9 +13,7 @@ import { cn } from "./cn.js";
|
|
|
13
13
|
* signification reste atteignable, elle n'occupe plus la place.
|
|
14
14
|
*/
|
|
15
15
|
export function SegmentedControl({ value, onValueChange, options, size = "md", label, className, }) {
|
|
16
|
-
return (_jsx("div", { role: "group", "aria-label": label, className: cn(
|
|
17
|
-
// Fond `muted` plutôt qu'un contour (ADR 0013).
|
|
18
|
-
"inline-flex w-fit max-w-full shrink-0 overflow-hidden rounded-3xl bg-muted p-1", className), children: options.map((opt) => {
|
|
16
|
+
return (_jsx("div", { role: "group", "aria-label": label, className: cn("inline-flex w-fit max-w-full shrink-0 overflow-hidden rounded-3xl border border-border bg-background p-1", className), children: options.map((opt) => {
|
|
19
17
|
const active = value === opt.value;
|
|
20
18
|
return (_jsx("button", { type: "button", "aria-pressed": active, "aria-label": opt.icon ? opt.label : undefined, title: opt.icon ? opt.label : undefined, onClick: () => onValueChange(opt.value), className: cn(
|
|
21
19
|
// Pas de flex-1 : une demi-colonne trop étroite rend le segment
|
package/dist/sonner.js
CHANGED
|
@@ -2,10 +2,10 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import Alert02Icon from "@hugeicons/core-free-icons/Alert02Icon";
|
|
3
3
|
import CheckmarkCircle02Icon from "@hugeicons/core-free-icons/CheckmarkCircle02Icon";
|
|
4
4
|
import InformationCircleIcon from "@hugeicons/core-free-icons/InformationCircleIcon";
|
|
5
|
-
import Loading03Icon from "@hugeicons/core-free-icons/Loading03Icon";
|
|
6
5
|
import MultiplicationSignCircleIcon from "@hugeicons/core-free-icons/MultiplicationSignCircleIcon";
|
|
7
6
|
import { HugeiconsIcon } from "@hugeicons/react";
|
|
8
7
|
import { Toaster as Sonner } from "sonner";
|
|
8
|
+
import { Spinner } from "./spinner.js";
|
|
9
9
|
import { useTheme } from "./theme.js";
|
|
10
10
|
function Toaster(props) {
|
|
11
11
|
const { theme } = useTheme();
|
|
@@ -18,7 +18,7 @@ function Toaster(props) {
|
|
|
18
18
|
info: _jsx(HugeiconsIcon, { icon: InformationCircleIcon, strokeWidth: 2, className: "size-4" }),
|
|
19
19
|
warning: _jsx(HugeiconsIcon, { icon: Alert02Icon, strokeWidth: 2, className: "size-4" }),
|
|
20
20
|
error: _jsx(HugeiconsIcon, { icon: MultiplicationSignCircleIcon, strokeWidth: 2, className: "size-4" }),
|
|
21
|
-
loading: _jsx(
|
|
21
|
+
loading: (_jsx("span", { "aria-hidden": true, children: _jsx(Spinner, {}) })),
|
|
22
22
|
}, style: {
|
|
23
23
|
"--normal-bg": "var(--popover)",
|
|
24
24
|
"--normal-text": "var(--popover-foreground)",
|
package/dist/spinner.d.ts
CHANGED
|
@@ -1,9 +1,20 @@
|
|
|
1
|
+
declare const SIZES: {
|
|
2
|
+
readonly 3: "size-3";
|
|
3
|
+
readonly 4: "size-4";
|
|
4
|
+
readonly 5: "size-5";
|
|
5
|
+
readonly 6: "size-6";
|
|
6
|
+
readonly 8: "size-8";
|
|
7
|
+
};
|
|
1
8
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
9
|
+
* L'anneau d'attente de la DA : un arc arrondi qui tourne sur une piste à peine visible, le
|
|
10
|
+
* même que la page d'attente du portail. Dans un bouton ou une ligne, il prend la couleur du
|
|
11
|
+
* texte (`tone="current"`) ; seul sur une page, il prend l'accent (`tone="accent"`).
|
|
12
|
+
* Mouvement réduit : l'arc ne tourne plus, il respire.
|
|
4
13
|
*/
|
|
5
|
-
export declare function Spinner({ className, label, size, }: {
|
|
14
|
+
export declare function Spinner({ className, label, size, tone, }: {
|
|
6
15
|
className?: string;
|
|
7
16
|
label?: string;
|
|
8
|
-
size?:
|
|
17
|
+
size?: keyof typeof SIZES;
|
|
18
|
+
tone?: "current" | "accent";
|
|
9
19
|
}): React.JSX.Element;
|
|
20
|
+
export {};
|
package/dist/spinner.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import Loading03Icon from "@hugeicons/core-free-icons/Loading03Icon";
|
|
3
|
-
import { HugeiconsIcon } from "@hugeicons/react";
|
|
4
2
|
import { cn } from "./cn.js";
|
|
3
|
+
const SIZES = { 3: "size-3", 4: "size-4", 5: "size-5", 6: "size-6", 8: "size-8" };
|
|
5
4
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* L'anneau d'attente de la DA : un arc arrondi qui tourne sur une piste à peine visible, le
|
|
6
|
+
* même que la page d'attente du portail. Dans un bouton ou une ligne, il prend la couleur du
|
|
7
|
+
* texte (`tone="current"`) ; seul sur une page, il prend l'accent (`tone="accent"`).
|
|
8
|
+
* Mouvement réduit : l'arc ne tourne plus, il respire.
|
|
8
9
|
*/
|
|
9
|
-
export function Spinner({ className, label, size = 4, }) {
|
|
10
|
-
return (_jsxs("span", { className: cn("inline-flex items-center gap-2", className), children: [
|
|
10
|
+
export function Spinner({ className, label, size = 4, tone = "current", }) {
|
|
11
|
+
return (_jsxs("span", { role: "status", className: cn("inline-flex items-center gap-2", className), children: [_jsxs("svg", { viewBox: "0 0 32 32", fill: "none", "aria-hidden": true, className: cn("shrink-0", SIZES[size]), children: [_jsx("circle", { cx: "16", cy: "16", r: "13", strokeWidth: "4", className: "stroke-current opacity-15" }), _jsx("circle", { cx: "16", cy: "16", r: "13", strokeWidth: "4", strokeLinecap: "round", strokeDasharray: "22 60", className: cn("origin-center animate-spin motion-reduce:animate-pulse", tone === "accent" ? "stroke-accent" : "stroke-current") })] }), label ? _jsx("span", { children: label }) : _jsx("span", { className: "sr-only", children: "Chargement" })] }));
|
|
11
12
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hgdc/ui",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Interface de la H family : jetons de la DA, thèmes, polices et composants.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"homepage": "https://github.com/hugogebel-boop/hgdc-kit/tree/main/packages/ui#readme",
|
package/styles.css
CHANGED
|
@@ -55,7 +55,6 @@
|
|
|
55
55
|
--color-border: var(--border);
|
|
56
56
|
--color-input: var(--input);
|
|
57
57
|
--color-ring: var(--ring);
|
|
58
|
-
--color-surface-hover: var(--surface-hover);
|
|
59
58
|
--color-shell: var(--shell);
|
|
60
59
|
--color-shell-foreground: var(--shell-foreground);
|
|
61
60
|
--color-shell-muted: var(--shell-muted-foreground);
|
|
@@ -66,6 +65,10 @@
|
|
|
66
65
|
--color-status-active: var(--status-active);
|
|
67
66
|
--color-status-paused: var(--status-paused);
|
|
68
67
|
--color-status-done: var(--status-done);
|
|
68
|
+
--color-status-active-ink: var(--status-active-ink);
|
|
69
|
+
--color-status-paused-ink: var(--status-paused-ink);
|
|
70
|
+
--color-info: var(--info);
|
|
71
|
+
--color-info-ink: var(--info-ink);
|
|
69
72
|
--color-chart-1: var(--chart-1);
|
|
70
73
|
--color-inverse: var(--inverse);
|
|
71
74
|
--color-inverse-foreground: var(--inverse-foreground);
|
|
@@ -87,26 +90,27 @@
|
|
|
87
90
|
--foreground: oklch(0.145 0 0);
|
|
88
91
|
--card: oklch(1 0 0);
|
|
89
92
|
--card-foreground: oklch(0.145 0 0);
|
|
90
|
-
|
|
91
|
-
--popover
|
|
93
|
+
/* Synonymes gardés pour les primitives shadcn : ils suivent leur jeton de rôle. */
|
|
94
|
+
--popover: var(--card);
|
|
95
|
+
--popover-foreground: var(--card-foreground);
|
|
92
96
|
/* Menus et sélecteurs (`floating.ts`) : fond opaque, un cran au-dessus de la carte en sombre. */
|
|
93
97
|
--floating: oklch(1 0 0);
|
|
94
98
|
--floating-muted: oklch(0.97 0 0);
|
|
95
99
|
--primary: oklch(0.205 0 0);
|
|
96
100
|
--primary-foreground: oklch(0.985 0 0);
|
|
97
|
-
--secondary:
|
|
101
|
+
--secondary: var(--muted);
|
|
98
102
|
--secondary-foreground: oklch(0.205 0 0);
|
|
99
103
|
--muted: oklch(0.97 0 0);
|
|
100
104
|
/* L=0.52 : 4,5:1 sur `muted`. */
|
|
101
105
|
--muted-foreground: oklch(0.52 0 0);
|
|
102
|
-
|
|
103
|
-
--accent
|
|
106
|
+
/* `--brand-accent*` : couleur d’un client posée par le produit, sans toucher au kit. */
|
|
107
|
+
--accent: var(--brand-accent, oklch(0.55 0.25 296));
|
|
108
|
+
--accent-foreground: var(--brand-accent-foreground, oklch(0.985 0 0));
|
|
104
109
|
--accent-soft: color-mix(in oklch, var(--accent) 10%, transparent);
|
|
105
110
|
--destructive: oklch(0.577 0.245 27.325);
|
|
106
111
|
--border: oklch(0.922 0 0);
|
|
107
112
|
--input: oklch(0.922 0 0);
|
|
108
113
|
--ring: var(--accent);
|
|
109
|
-
--surface-hover: oklch(0.945 0 0);
|
|
110
114
|
/* Coque de l'application : barre latérale et en-tête. */
|
|
111
115
|
--shell: oklch(0.955 0 0);
|
|
112
116
|
--shell-foreground: oklch(0.145 0 0);
|
|
@@ -117,6 +121,12 @@
|
|
|
117
121
|
--status-active: oklch(0.55 0.15 156);
|
|
118
122
|
--status-paused: oklch(0.62 0.14 75);
|
|
119
123
|
--status-done: oklch(0.52 0.2 293);
|
|
124
|
+
/* Encres : le texte d'un statut sur son lavis (4,5:1), la couleur vive restant aux pastilles. */
|
|
125
|
+
--status-active-ink: oklch(0.45 0.12 155);
|
|
126
|
+
--status-paused-ink: oklch(0.5 0.12 70);
|
|
127
|
+
/* Information : bleu, pour que le violet reste l'accent. */
|
|
128
|
+
--info: oklch(0.62 0.19 256);
|
|
129
|
+
--info-ink: oklch(0.48 0.17 256);
|
|
120
130
|
--success: oklch(0.58 0.15 152);
|
|
121
131
|
--success-foreground: oklch(0.985 0 0);
|
|
122
132
|
/* Panneau inversé de la vitrine : encre sombre dans les deux thèmes. */
|
|
@@ -133,22 +143,18 @@
|
|
|
133
143
|
--foreground: oklch(0.985 0 0);
|
|
134
144
|
--card: oklch(0.205 0 0);
|
|
135
145
|
--card-foreground: oklch(0.985 0 0);
|
|
136
|
-
--popover: oklch(0.205 0 0);
|
|
137
|
-
--popover-foreground: oklch(0.985 0 0);
|
|
138
146
|
--floating: oklch(0.255 0 0);
|
|
139
147
|
--floating-muted: oklch(0.315 0 0);
|
|
140
148
|
--primary: oklch(0.922 0 0);
|
|
141
149
|
--primary-foreground: oklch(0.205 0 0);
|
|
142
|
-
--secondary: oklch(0.269 0 0);
|
|
143
150
|
--secondary-foreground: oklch(0.985 0 0);
|
|
144
151
|
--muted: oklch(0.269 0 0);
|
|
145
152
|
--muted-foreground: oklch(0.708 0 0);
|
|
146
|
-
--accent: oklch(0.74 0.19 296);
|
|
147
|
-
--accent-foreground: oklch(0.205 0 0);
|
|
153
|
+
--accent: var(--brand-accent-dark, oklch(0.74 0.19 296));
|
|
154
|
+
--accent-foreground: var(--brand-accent-dark-foreground, oklch(0.205 0 0));
|
|
148
155
|
--destructive: oklch(0.704 0.191 22.216);
|
|
149
156
|
--border: oklch(1 0 0 / 10%);
|
|
150
157
|
--input: oklch(1 0 0 / 15%);
|
|
151
|
-
--surface-hover: oklch(0.25 0 0);
|
|
152
158
|
--shell: oklch(0.08 0 0);
|
|
153
159
|
--shell-foreground: oklch(0.985 0 0);
|
|
154
160
|
--shell-muted-foreground: oklch(0.708 0 0);
|
|
@@ -158,6 +164,10 @@
|
|
|
158
164
|
--status-active: oklch(0.74 0.14 156);
|
|
159
165
|
--status-paused: oklch(0.82 0.15 86);
|
|
160
166
|
--status-done: oklch(0.68 0.17 293);
|
|
167
|
+
--status-active-ink: var(--status-active);
|
|
168
|
+
--status-paused-ink: var(--status-paused);
|
|
169
|
+
--info: oklch(0.7 0.16 256);
|
|
170
|
+
--info-ink: var(--info);
|
|
161
171
|
--success: oklch(0.7 0.15 152);
|
|
162
172
|
--success-foreground: oklch(0.16 0.02 152);
|
|
163
173
|
--inverse: oklch(0.1 0 0);
|
|
@@ -209,15 +219,11 @@
|
|
|
209
219
|
|
|
210
220
|
/* ── Surfaces ──────────────────────────────────────────────────────────── */
|
|
211
221
|
|
|
212
|
-
/* Niveau 1 de la DA : une teinte, ni ombre ni bordure. `muted` en clair, `card` en sombre
|
|
213
|
-
|
|
214
|
-
la carte. En utilitaire pour qu'un `hover:` posé sur la carte garde la main. */
|
|
222
|
+
/* Niveau 1 de la DA : une teinte, ni ombre ni bordure. `muted` en clair, `card` en sombre
|
|
223
|
+
(un cran plus clair que la page). */
|
|
215
224
|
@utility surface {
|
|
216
225
|
background-color: var(--muted);
|
|
217
|
-
&
|
|
218
|
-
--muted: var(--background);
|
|
219
|
-
}
|
|
220
|
-
.dark & {
|
|
226
|
+
:where(.dark) & {
|
|
221
227
|
background-color: var(--card);
|
|
222
228
|
}
|
|
223
229
|
}
|
|
@@ -227,12 +233,12 @@
|
|
|
227
233
|
background-color: var(--muted);
|
|
228
234
|
transition: background-color var(--duration-fast) var(--ease-out);
|
|
229
235
|
&:hover {
|
|
230
|
-
background-color:
|
|
236
|
+
background-color: oklch(0.94 0 0);
|
|
231
237
|
}
|
|
232
|
-
.dark & {
|
|
238
|
+
:where(.dark) & {
|
|
233
239
|
background-color: var(--card);
|
|
234
240
|
}
|
|
235
|
-
.dark &:hover {
|
|
241
|
+
:where(.dark) &:hover {
|
|
236
242
|
background-color: var(--muted);
|
|
237
243
|
}
|
|
238
244
|
}
|