skcr-design-tokens 1.0.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/README.md +122 -0
- package/components.css +243 -0
- package/display.css +44 -0
- package/package.json +19 -0
- package/tokens.css +199 -0
- package/utilities.css +49 -0
package/README.md
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# skcr-design-tokens
|
|
2
|
+
|
|
3
|
+
Le design system SkillCorner, en CSS pur et sans dépendance : tokens de couleur,
|
|
4
|
+
utilitaires typographiques et classes de composants. Source unique pour orion et
|
|
5
|
+
gaffer, et contrat de rendu de la librairie de composants `skcr-autechre`.
|
|
6
|
+
|
|
7
|
+
Aucune dépendance, aucun build : le paquet publie les fichiers `.css` tels quels.
|
|
8
|
+
Il est donc consommable par n'importe quel front — Angular, React ou vanilla.
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install skcr-design-tokens
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Dans le `styles.css` (ou `styles.scss`) de l'application :
|
|
17
|
+
|
|
18
|
+
```css
|
|
19
|
+
@import 'tailwindcss';
|
|
20
|
+
@import 'skcr-design-tokens/tokens.css';
|
|
21
|
+
@import 'skcr-design-tokens/utilities.css';
|
|
22
|
+
@import 'skcr-design-tokens/components.css';
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Applications qui consomment skcr-autechre
|
|
26
|
+
|
|
27
|
+
Tailwind ne scanne pas `node_modules`. Les classes que les composants de la
|
|
28
|
+
librairie écrivent dans leurs templates ne seraient donc jamais générées, et les
|
|
29
|
+
styles manqueraient sans qu'aucune erreur ne le signale. Une ligne suffit :
|
|
30
|
+
|
|
31
|
+
```css
|
|
32
|
+
@source '../node_modules/skcr-autechre/fesm2022/*.mjs';
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Le chemin vise le JavaScript **construit**, où ng-packagr a inliné les templates :
|
|
36
|
+
c'est là que les classes se trouvent dans le paquet publié.
|
|
37
|
+
|
|
38
|
+
## Thème clair / sombre
|
|
39
|
+
|
|
40
|
+
L'attribut se pose sur `<html>`, **jamais sur `<body>`**. Tailwind émet le bloc
|
|
41
|
+
`@theme` dans `:root`, c'est-à-dire sur `<html>` ; or une custom property est
|
|
42
|
+
substituée à l'élément où elle est déclarée. Une surcharge posée sur `<body>`
|
|
43
|
+
serait invisible pour les utilitaires, qui hériteraient d'une valeur déjà
|
|
44
|
+
résolue avec la palette claire — les couleurs écrites à la main basculeraient,
|
|
45
|
+
les classes Tailwind non.
|
|
46
|
+
|
|
47
|
+
Pour éviter tout scintillement au chargement, l'attribut doit être posé **avant
|
|
48
|
+
la première peinture**. Aucun mécanisme applicatif ne peut le faire : un
|
|
49
|
+
initialiseur Angular s'exécute après le premier rendu. Coller ce script dans
|
|
50
|
+
`index.html`, dans le `<head>` :
|
|
51
|
+
|
|
52
|
+
```html
|
|
53
|
+
<script>
|
|
54
|
+
try {
|
|
55
|
+
document.documentElement.dataset.theme = localStorage.getItem('skcr-theme') || 'light';
|
|
56
|
+
} catch {
|
|
57
|
+
document.documentElement.dataset.theme = 'light';
|
|
58
|
+
}
|
|
59
|
+
</script>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Les applications Angular utilisent ensuite le `ThemeService` de `skcr-autechre`
|
|
63
|
+
pour la bascule et la synchronisation entre onglets. Il ne fait pas
|
|
64
|
+
l'initialisation : le script ci-dessus l'a déjà faite.
|
|
65
|
+
|
|
66
|
+
## Contenu
|
|
67
|
+
|
|
68
|
+
| Entrée | Contenu |
|
|
69
|
+
| --- | --- |
|
|
70
|
+
| `tokens.css` | Bloc `@theme` : couleurs primaires, neutres, de statut, de dataviz, et familles de polices |
|
|
71
|
+
| `utilities.css` | `text-body-*`, `text-caption`, `info-caption`, `title-one` à `title-five` |
|
|
72
|
+
| `components.css` | Famille `.btn`, utilitaires de barre de défilement, correction Angular Material du sélecteur de date |
|
|
73
|
+
| `display.css` | **Opt-in.** Classes de présentation. Aucune application du socle ne l'importe. |
|
|
74
|
+
|
|
75
|
+
## Ce qui reste dans l'application
|
|
76
|
+
|
|
77
|
+
Trois catégories ne montent pas ici, et c'est délibéré :
|
|
78
|
+
|
|
79
|
+
- **Les `@font-face`.** Shentox est une police commerciale : la redistribuer
|
|
80
|
+
dans un paquet npm public serait une violation de licence. Le paquet déclare
|
|
81
|
+
la famille (`--font-title`), l'application fournit les fichiers.
|
|
82
|
+
- **Les resets globaux.** `html, body { height: 100vh; overflow: hidden }` est
|
|
83
|
+
une décision d'application monopage, pas une décision de design.
|
|
84
|
+
- **Les tokens de domaine.** `--color-tactical` (gaffer), `--color-tooltip-*`
|
|
85
|
+
(orion) : ils ne servent qu'à une application.
|
|
86
|
+
|
|
87
|
+
## Règles d'extension
|
|
88
|
+
|
|
89
|
+
La distinction est nette, et c'est elle qui empêche le design system de
|
|
90
|
+
re-diverger comme il l'a fait entre les trois applications :
|
|
91
|
+
|
|
92
|
+
**Surcharger une valeur est libre.** Une application peut redéfinir n'importe
|
|
93
|
+
quel token dans son propre `styles.css`, après les `@import` :
|
|
94
|
+
|
|
95
|
+
```css
|
|
96
|
+
:root {
|
|
97
|
+
--color-primary: #0f766e;
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
C'est le mécanisme du thème sombre, et c'est ce qui permet à un projet de se
|
|
102
|
+
repeindre sans forker le paquet. Le contrat reste le même, seules les valeurs
|
|
103
|
+
changent.
|
|
104
|
+
|
|
105
|
+
**Définir un utilitaire ne l'est pas.** Ajouter un `@utility`, une classe
|
|
106
|
+
`.btn-*` ou une classe de présentation dans le `styles.css` d'une application
|
|
107
|
+
est interdit : c'est exactement comme ça que `.btn-icon` s'est retrouvé dans
|
|
108
|
+
orion seul, et que la famille `.btn` a divergé sur cinq points. Une nouvelle
|
|
109
|
+
définition passe par une merge request sur ce paquet.
|
|
110
|
+
|
|
111
|
+
**Ajouter dans `display.css`.** C'est le fichier prévu pour les besoins neufs,
|
|
112
|
+
notamment ceux des projets qui n'ont pas de design validé. Il vise les **projets vibe-codés**, à qui il donne une uniformité de façade ; les
|
|
113
|
+
applications de la plateforme ont leurs propres besoins, tranchés par le product
|
|
114
|
+
design, et ne l'importent pas. Trois règles :
|
|
115
|
+
|
|
116
|
+
1. Une classe n'utilise que les tokens de `tokens.css`. Aucune couleur en dur —
|
|
117
|
+
c'est ce qui garantit qu'elle suivra le thème sombre sans retouche.
|
|
118
|
+
2. Une classe ne redéfinit jamais une classe du socle. En cas de doute sur un
|
|
119
|
+
nom, vérifier dans `components.css`.
|
|
120
|
+
3. Ne pas écrire pour le socle. Ces classes n'ont pas vocation à y remonter : ce
|
|
121
|
+
n'est pas une antichambre. Si l'une devait un jour servir orion ou gaffer, ce
|
|
122
|
+
serait une décision explicite et exceptionnelle, pas un glissement.
|
package/components.css
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/* ---------------------------------------------------------------------------
|
|
2
|
+
Classes de composants du design system SkillCorner.
|
|
3
|
+
|
|
4
|
+
Exige `tokens.css` et `utilities.css`.
|
|
5
|
+
|
|
6
|
+
La famille `.btn` reprend la variante d'orion, choisie comme référence.
|
|
7
|
+
gaffer s'y aligne : ombre portée, base en `py-2`,
|
|
8
|
+
`.btn-mini` en `py-1.5`, survol neutre en `bg-slate-100`, et `.btn-selected`
|
|
9
|
+
sans `px-2.5`. `.btn-input` vient de gaffer, qui l'emploie ; `.btn-select`
|
|
10
|
+
n'est repris nulle part : il était défini sans être utilisé nulle part.
|
|
11
|
+
|
|
12
|
+
La famille `.btn` est passée aux tokens sémantiques. Le rendu en thème clair
|
|
13
|
+
est identique au précédent, à une exception près : le survol de
|
|
14
|
+
`.btn-transparent` passe de slate-50 à slate-100, d'un cran. Les survols ont
|
|
15
|
+
leurs propres tokens plutôt qu'une opacité, parce qu'en sombre le fond
|
|
16
|
+
primaire est CLAIR : son survol doit s'éclaircir, là où en clair il assombrit.
|
|
17
|
+
|
|
18
|
+
La famille `.badge` garde en revanche ses teintes brutes, et c'est délibéré :
|
|
19
|
+
`badge-purple` est violet par définition, pas par rôle. Ses pastilles sont des
|
|
20
|
+
fonds clairs à texte sombre, ce qui reste lisible sur une interface sombre.
|
|
21
|
+
|
|
22
|
+
`.badge` reprend la variante de gaffer, seule à être réellement employée — 38
|
|
23
|
+
usages. La variante écartée portait des classes `dark:` qui n'auraient de toute
|
|
24
|
+
façon pas répondu au `data-theme`.
|
|
25
|
+
`.link`, `.step-number` (gaffer) et le motif `.has-tooltip` (orion) montent
|
|
26
|
+
ici : génériques et additifs, aucune application n'y perd quelque chose.
|
|
27
|
+
|
|
28
|
+
`.spinner` NE monte PAS, et c'est délibéré : orion en définit un global qui
|
|
29
|
+
duplique le style encapsulé du `skcr-submit-button`, avec une bordure haute
|
|
30
|
+
grise au lieu de blanche. Une règle globale et un style de composant ont la
|
|
31
|
+
même spécificité — le vainqueur dépend de l'ordre d'injection. Le monter ici
|
|
32
|
+
repeindrait le spinner du bouton de soumission dans les trois applications.
|
|
33
|
+
À terme, orion devrait abandonner le sien au profit du composant.
|
|
34
|
+
|
|
35
|
+
`.mat-datepicker-content-container` n'est pas du design system : c'est une
|
|
36
|
+
correction Angular Material qu'exige le `skcr-date-picker` de la librairie.
|
|
37
|
+
Elle vit ici parce que les trois applications la portent déjà à l'identique
|
|
38
|
+
et qu'une librairie Angular ne peut pas livrer de style global.
|
|
39
|
+
--------------------------------------------------------------------------- */
|
|
40
|
+
|
|
41
|
+
@layer components {
|
|
42
|
+
.mat-datepicker-content-container {
|
|
43
|
+
background-color: var(--color-popover);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.btn {
|
|
47
|
+
@apply rounded-md leading-5 shadow-sm items-center flex gap-1 justify-center py-2 px-3 max-w-sm text-body-small outline-1 -outline-offset-1 outline-border-color focus:outline-2 focus:-outline-offset-2 font-medium relative overflow-hidden z-0 cursor-pointer disabled:cursor-not-allowed disabled:opacity-50;
|
|
48
|
+
|
|
49
|
+
&.btn-large {
|
|
50
|
+
@apply px-4 py-2.5;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
&.btn-mini {
|
|
54
|
+
@apply px-2.5 py-1.5;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
&.btn-icon {
|
|
58
|
+
@apply size-8 p-0 rounded-lg shadow-none;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
&.btn-input {
|
|
62
|
+
@apply py-2;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
&.btn-full {
|
|
66
|
+
@apply max-w-full;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
&.btn-selected {
|
|
70
|
+
@apply bg-primary-subtle text-primary-subtle-foreground;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
&.btn-primary {
|
|
74
|
+
@apply bg-primary text-primary-foreground hover:bg-primary-hover focus:outline-primary;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
&.btn-neutral {
|
|
78
|
+
@apply bg-card hover:bg-muted;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
&.btn-transparent {
|
|
82
|
+
@apply hover:bg-muted outline-0;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
&.btn-danger {
|
|
86
|
+
@apply bg-destructive text-destructive-foreground hover:bg-destructive-hover;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.badge {
|
|
91
|
+
@apply inline-flex items-center justify-center gap-0.5 rounded-lg border border-transparent bg-slate-100 px-2 py-0.5 text-xs leading-4 font-medium text-slate-950;
|
|
92
|
+
|
|
93
|
+
&.badge-primary {
|
|
94
|
+
@apply border-primary-300 bg-primary-100;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
&.badge-green {
|
|
98
|
+
@apply border-green-300 bg-green-100;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
&.badge-yellow {
|
|
102
|
+
@apply border-yellow-300 bg-yellow-100;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
&.badge-orange {
|
|
106
|
+
@apply border-orange-300 bg-orange-100;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
&.badge-destructive {
|
|
110
|
+
@apply border-red-300 bg-red-100;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
&.badge-blue {
|
|
114
|
+
@apply border-blue-300 bg-blue-100;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
&.badge-purple {
|
|
118
|
+
@apply border-purple-300 bg-purple-100;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
&.badge-pink {
|
|
122
|
+
@apply border-pink-300 bg-pink-100;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
&.badge-cyan {
|
|
126
|
+
@apply border-cyan-300 bg-cyan-100;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
&.badge-strong-yellow {
|
|
130
|
+
@apply bg-yellow-300;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
&.badge-strong-orange {
|
|
134
|
+
@apply bg-orange-300;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
&.badge-strong-red {
|
|
138
|
+
@apply bg-red-300;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
&.badge-strong-slate {
|
|
142
|
+
@apply bg-slate-200;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.link {
|
|
147
|
+
@apply text-primary underline cursor-pointer;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.step-number {
|
|
151
|
+
@apply flex h-8 w-8 items-center justify-center rounded-full bg-primary p-2 text-xl text-primary-foreground;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.tooltip {
|
|
155
|
+
@apply invisible absolute;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.has-tooltip:hover .tooltip {
|
|
159
|
+
@apply visible z-50;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.has-tooltip {
|
|
163
|
+
@apply relative;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.scrollbar-hide {
|
|
167
|
+
/* IE et Edge */
|
|
168
|
+
-ms-overflow-style: none;
|
|
169
|
+
/* Firefox */
|
|
170
|
+
scrollbar-width: none;
|
|
171
|
+
/* Safari et Chrome */
|
|
172
|
+
&::-webkit-scrollbar {
|
|
173
|
+
display: none;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.scrollbar-hide-y {
|
|
178
|
+
/* IE et Edge — impossible de masquer la seule barre verticale */
|
|
179
|
+
-ms-overflow-style: none;
|
|
180
|
+
/* Firefox */
|
|
181
|
+
scrollbar-width: none;
|
|
182
|
+
/* Safari et Chrome */
|
|
183
|
+
&::-webkit-scrollbar {
|
|
184
|
+
height: auto; /* barre horizontale conservée */
|
|
185
|
+
width: 0; /* barre verticale masquée */
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.scrollbar-hide-x {
|
|
190
|
+
/* IE et Edge — impossible de masquer la seule barre horizontale */
|
|
191
|
+
-ms-overflow-style: none;
|
|
192
|
+
/* Firefox */
|
|
193
|
+
scrollbar-width: none;
|
|
194
|
+
/* Safari et Chrome */
|
|
195
|
+
&::-webkit-scrollbar {
|
|
196
|
+
height: 0; /* barre horizontale masquée */
|
|
197
|
+
width: auto; /* barre verticale conservée */
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.scrollbar-default {
|
|
202
|
+
/* IE et Edge */
|
|
203
|
+
-ms-overflow-style: auto;
|
|
204
|
+
/* Firefox */
|
|
205
|
+
scrollbar-width: auto;
|
|
206
|
+
/* Safari et Chrome */
|
|
207
|
+
&::-webkit-scrollbar {
|
|
208
|
+
display: block;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/* ---------------------------------------------------------------------------
|
|
214
|
+
Angular Material dérive tout son habillage des tokens système `--mat-sys-*`,
|
|
215
|
+
dont son thème préfabriqué fixe la teinte. Sans cette redirection, le
|
|
216
|
+
calendrier et le panneau d'heures de `skcr-date-picker` et
|
|
217
|
+
`skcr-datetime-picker` sortent en bleu azur au milieu d'une interface verte.
|
|
218
|
+
|
|
219
|
+
Les variables pointent vers les tokens du design system plutôt que d'en
|
|
220
|
+
recopier les valeurs : un changement de charte — ou de thème — les suit.
|
|
221
|
+
C'est pour cette raison que la surface vise `--color-popover` et non un
|
|
222
|
+
blanc littéral : le calendrier est un panneau flottant, il doit suivre la
|
|
223
|
+
même surface que les autres.
|
|
224
|
+
|
|
225
|
+
Volontairement hors couche : Material déclare ses propres `--mat-sys-*` sans
|
|
226
|
+
`@layer`, et une déclaration layerisée perdrait l'arbitrage de cascade.
|
|
227
|
+
|
|
228
|
+
Inerte pour une application qui n'affiche aucun composant Material : ni orion ni
|
|
229
|
+
gaffer n'importe Material directement. gaffer, en
|
|
230
|
+
revanche, affiche un `skcr-datetime-picker` sans cette recette — ce picker
|
|
231
|
+
rend donc aujourd'hui en bleu.
|
|
232
|
+
--------------------------------------------------------------------------- */
|
|
233
|
+
:root {
|
|
234
|
+
--mat-sys-primary: var(--color-primary);
|
|
235
|
+
--mat-sys-on-primary: var(--color-primary-foreground);
|
|
236
|
+
--mat-sys-surface: var(--color-popover);
|
|
237
|
+
--mat-sys-surface-container-high: var(--color-popover);
|
|
238
|
+
--mat-sys-on-surface: var(--color-foreground);
|
|
239
|
+
--mat-sys-on-surface-variant: var(--color-muted-foreground);
|
|
240
|
+
--mat-sys-outline: var(--color-border-color);
|
|
241
|
+
--mat-sys-outline-variant: var(--color-border-color);
|
|
242
|
+
--mat-sys-corner-large: 0.5rem;
|
|
243
|
+
}
|
package/display.css
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/* ---------------------------------------------------------------------------
|
|
2
|
+
Classes de présentation — entrée OPT-IN.
|
|
3
|
+
|
|
4
|
+
Ce fichier n'est importé par aucune application de la plateforme : ni orion, ni
|
|
5
|
+
gaffer n'en dépendent. Il existe pour donner une uniformité de façade aux PROJETS
|
|
6
|
+
VIBE-CODÉS — qu'un outil monté vite ne réinvente pas sa carte, son badge et sa
|
|
7
|
+
pastille à chaque fois. C'est un garde-fou, pas un catalogue de composants.
|
|
8
|
+
|
|
9
|
+
Ces classes n'ont PAS vocation à remonter un jour dans le socle : ce n'est pas une
|
|
10
|
+
antichambre. Un besoin de la plateforme se traite par le product design, pas en
|
|
11
|
+
promouvant une classe écrite pour un autre usage.
|
|
12
|
+
|
|
13
|
+
Trois règles, détaillées dans le README :
|
|
14
|
+
1. Aucune couleur en dur — uniquement les tokens de `tokens.css`. C'est ce qui
|
|
15
|
+
garantit qu'une classe suivra le thème sombre sans retouche.
|
|
16
|
+
2. Aucune redéfinition d'une classe du socle : vérifier `components.css`.
|
|
17
|
+
3. Ne pas écrire pour le socle. Si une classe devait un jour servir orion ou gaffer,
|
|
18
|
+
ce serait une décision explicite et exceptionnelle, jamais un glissement.
|
|
19
|
+
|
|
20
|
+
Les deux classes ci-dessous sont des exemples de référence, tirés de motifs
|
|
21
|
+
réellement répétés dans les applications, pas inventés :
|
|
22
|
+
|
|
23
|
+
- `.card` : 19 fichiers d'orion écrivent `bg-white border border-border-color
|
|
24
|
+
rounded-lg p-5` à la main, aucun composant partagé ne le couvre. La classe
|
|
25
|
+
porte la SURFACE ; la mise en page (`flex`, `gap-4`, `flex-1`, `mb-8`, qui
|
|
26
|
+
varient d'un usage à l'autre) reste dans le template.
|
|
27
|
+
- `.avatar` : reprend la géométrie du `user-avatar` d'orion. Le repli sur les
|
|
28
|
+
initiales, lui, porte de la logique — c'est le premier candidat à un vrai
|
|
29
|
+
composant, une fois la classe confrontée à l'usage.
|
|
30
|
+
|
|
31
|
+
Noter que `bg-card` et `border-background` remplacent ici les `bg-white` et
|
|
32
|
+
`border-white` de l'existant : à valeur identique aujourd'hui, mais suivant le
|
|
33
|
+
thème demain. C'est la règle 1 en pratique.
|
|
34
|
+
--------------------------------------------------------------------------- */
|
|
35
|
+
|
|
36
|
+
@layer components {
|
|
37
|
+
.card {
|
|
38
|
+
@apply bg-card border border-border-color rounded-lg p-5;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.avatar {
|
|
42
|
+
@apply size-8 rounded-full object-cover border-2 border-background;
|
|
43
|
+
}
|
|
44
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "skcr-design-tokens",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Design system SkillCorner : tokens de couleur, utilitaires typographiques et classes de composants. CSS pur, sans dépendance.",
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"sideEffects": [
|
|
7
|
+
"*.css"
|
|
8
|
+
],
|
|
9
|
+
"files": [
|
|
10
|
+
"*.css",
|
|
11
|
+
"README.md"
|
|
12
|
+
],
|
|
13
|
+
"exports": {
|
|
14
|
+
"./tokens.css": "./tokens.css",
|
|
15
|
+
"./utilities.css": "./utilities.css",
|
|
16
|
+
"./components.css": "./components.css",
|
|
17
|
+
"./display.css": "./display.css"
|
|
18
|
+
}
|
|
19
|
+
}
|
package/tokens.css
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/* ---------------------------------------------------------------------------
|
|
2
|
+
Tokens du design system SkillCorner.
|
|
3
|
+
|
|
4
|
+
Source unique des valeurs qu'orion et gaffer recopiaient chacun dans son
|
|
5
|
+
`styles.scss`. Les couleurs primaires, neutres, de statut et de
|
|
6
|
+
dataviz y étaient déjà rigoureusement identiques dans les trois. Les tokens
|
|
7
|
+
d'infobulle ne venaient que d'orion : ils montent ici avec le motif
|
|
8
|
+
`.has-tooltip` de `components.css`, et sont donc additifs pour les deux
|
|
9
|
+
autres applications.
|
|
10
|
+
|
|
11
|
+
Délibérément absents, parce qu'ils n'appartiennent qu'à une application :
|
|
12
|
+
- `--color-tactical` (gaffer) : concept métier.
|
|
13
|
+
- `--animate-splash-*` et leurs `@keyframes` (orion) : écran de chargement.
|
|
14
|
+
- les `@font-face` Shentox : police commerciale, non redistribuable dans un
|
|
15
|
+
paquet npm public. Chaque application fournit les siens ; ce fichier ne
|
|
16
|
+
déclare que la famille.
|
|
17
|
+
|
|
18
|
+
Thème sombre — l'attribut se pose sur `<html>`, jamais sur `<body>` :
|
|
19
|
+
Tailwind émet ce bloc `@theme` dans `:root`, donc sur `<html>`. Une custom
|
|
20
|
+
property est substituée à l'élément où elle est déclarée ; une surcharge
|
|
21
|
+
posée sur `<body>` serait donc invisible pour les utilitaires, qui hériteraient
|
|
22
|
+
d'une valeur déjà résolue avec la palette claire. Sur `<html>`, les deux
|
|
23
|
+
déclarations vivent sur le même élément et la surcharge s'applique :
|
|
24
|
+
|
|
25
|
+
:root[data-theme='dark'] { --color-card: …; }
|
|
26
|
+
|
|
27
|
+
Le sélecteur d'attribut l'emporte sur le `:root` du bloc `@theme`, qui est
|
|
28
|
+
par ailleurs émis dans une couche (`@layer theme`) — les deux mécanismes
|
|
29
|
+
pointent dans le même sens.
|
|
30
|
+
--------------------------------------------------------------------------- */
|
|
31
|
+
|
|
32
|
+
@theme {
|
|
33
|
+
--font-sans: 'Inter', sans-serif;
|
|
34
|
+
--font-title: 'Shentox', serif;
|
|
35
|
+
|
|
36
|
+
/* Couleurs primaires */
|
|
37
|
+
--color-primary-50: #edfff1;
|
|
38
|
+
--color-primary-100: #d5ffe1;
|
|
39
|
+
--color-primary-200: #aeffc5;
|
|
40
|
+
--color-primary-300: #6fff9a;
|
|
41
|
+
--color-primary-400: #32fe6b;
|
|
42
|
+
--color-primary-500: #02e540;
|
|
43
|
+
--color-primary-600: #00bf30;
|
|
44
|
+
--color-primary-700: #008a26;
|
|
45
|
+
--color-primary-800: #057626;
|
|
46
|
+
--color-primary-900: #066122;
|
|
47
|
+
--color-primary: #008a26;
|
|
48
|
+
/* Survols. Un `/90` d'opacité aurait suffi en clair mais pas en sombre, où le fond
|
|
49
|
+
primaire est CLAIR : son survol doit s'éclaircir, pas s'assombrir. C'est pourquoi
|
|
50
|
+
chaque couleur d'action a son token de survol plutôt qu'une opacité. */
|
|
51
|
+
--color-primary-hover: #057626; /* primary-800 */
|
|
52
|
+
/* Libellé lisible SUR un fond primaire. En sombre le primaire s'éclaircit,
|
|
53
|
+
donc ce libellé s'assombrit : `bg-primary text-white` ne peut pas suivre
|
|
54
|
+
le thème, `bg-primary text-primary-foreground` si. */
|
|
55
|
+
--color-primary-foreground: theme(colors.white);
|
|
56
|
+
/* Surface primaire discrète : option sélectionnée d'une liste, pastille. */
|
|
57
|
+
--color-primary-subtle: #edfff1;
|
|
58
|
+
--color-primary-subtle-foreground: #057626;
|
|
59
|
+
|
|
60
|
+
/* Couleurs neutres */
|
|
61
|
+
--color-background: theme(colors.white);
|
|
62
|
+
--color-card: theme(colors.white);
|
|
63
|
+
--color-popover: theme(colors.white);
|
|
64
|
+
--color-card-foreground: theme(colors.slate.950);
|
|
65
|
+
--color-popover-foreground: theme(colors.slate.950);
|
|
66
|
+
--color-foreground: theme(colors.slate.950);
|
|
67
|
+
--color-muted: theme(colors.slate.100);
|
|
68
|
+
--color-muted-foreground: theme(colors.slate.500);
|
|
69
|
+
--color-border-color: theme(colors.slate.200);
|
|
70
|
+
--color-input: theme(colors.slate.200);
|
|
71
|
+
--color-ring: theme(colors.slate.950);
|
|
72
|
+
--color-subtitle: theme(colors.slate.500);
|
|
73
|
+
/* Fond d'un contrôle désactivé. Distinct de `muted`, qui est un fond de
|
|
74
|
+
zone : les deux valeurs sont voisines en clair mais divergent en sombre. */
|
|
75
|
+
--color-disabled: theme(colors.slate.50);
|
|
76
|
+
/* Fond de piste : conteneur d'onglets, onglet non sélectionné, rail d'un
|
|
77
|
+
interrupteur. Un cran plus marqué que `muted`, qui est un fond de zone. */
|
|
78
|
+
--color-track: theme(colors.slate.200);
|
|
79
|
+
/* Surface surélevée : ce qui est POSÉ sur une autre surface — onglet actif sur sa piste,
|
|
80
|
+
pastille d'interrupteur sur son rail, point d'un bouton radio coché sur son fond
|
|
81
|
+
primaire. À distinguer d'un CREUX, qui prend `background` : le cercle d'un radio non
|
|
82
|
+
coché, le fond d'un champ. En clair elle vaut blanc, comme le fond de page ; en sombre
|
|
83
|
+
elle doit être plus CLAIRE que ce qu'elle surmonte, sans quoi l'élément en relief
|
|
84
|
+
paraît creusé — et aucun autre token ne le permet, `popover` y valant exactement
|
|
85
|
+
`track`. */
|
|
86
|
+
--color-elevated: theme(colors.white);
|
|
87
|
+
/* Voile de modale. Seul token qui ne s'inverse pas : un voile doit assombrir
|
|
88
|
+
dans les deux thèmes, il ne peut donc pas suivre `foreground`. */
|
|
89
|
+
--color-scrim: theme(colors.slate.500);
|
|
90
|
+
|
|
91
|
+
/* Couleurs de statut */
|
|
92
|
+
--color-info: theme(colors.blue.700);
|
|
93
|
+
--color-info-hover: theme(colors.blue.800);
|
|
94
|
+
--color-info-foreground: theme(colors.blue.100);
|
|
95
|
+
--color-success: theme(colors.green.700);
|
|
96
|
+
--color-success-foreground: theme(colors.green.100);
|
|
97
|
+
--color-destructive: theme(colors.red.700);
|
|
98
|
+
--color-destructive-hover: theme(colors.red.800);
|
|
99
|
+
--color-destructive-foreground: theme(colors.red.100);
|
|
100
|
+
--color-warning: theme(colors.orange.700);
|
|
101
|
+
--color-warning-hover: theme(colors.orange.800);
|
|
102
|
+
--color-warning-foreground: theme(colors.orange.100);
|
|
103
|
+
--color-neutral: theme(colors.slate.700);
|
|
104
|
+
--color-neutral-foreground: theme(colors.slate.100);
|
|
105
|
+
|
|
106
|
+
/* Infobulles — consommés par le motif `.has-tooltip` de components.css */
|
|
107
|
+
--color-tooltip-surface: theme(colors.slate.800);
|
|
108
|
+
--color-tooltip-border: theme(colors.slate.500);
|
|
109
|
+
--color-tooltip-foreground: theme(colors.white);
|
|
110
|
+
--color-tooltip-subtitle: theme(colors.slate.200);
|
|
111
|
+
|
|
112
|
+
/* Couleurs de dataviz */
|
|
113
|
+
--color-chart-1: theme(colors.green.500);
|
|
114
|
+
--color-chart-1-accent: theme(colors.green.300);
|
|
115
|
+
--color-chart-1-foreground: theme(colors.green.100);
|
|
116
|
+
--color-chart-2: theme(colors.lime.500);
|
|
117
|
+
--color-chart-2-accent: theme(colors.lime.300);
|
|
118
|
+
--color-chart-2-foreground: theme(colors.lime.100);
|
|
119
|
+
--color-chart-3: theme(colors.yellow.500);
|
|
120
|
+
--color-chart-3-accent: theme(colors.yellow.300);
|
|
121
|
+
--color-chart-3-foreground: theme(colors.yellow.100);
|
|
122
|
+
--color-chart-4: theme(colors.orange.500);
|
|
123
|
+
--color-chart-4-accent: theme(colors.orange.300);
|
|
124
|
+
--color-chart-4-foreground: theme(colors.orange.100);
|
|
125
|
+
--color-chart-5: theme(colors.red.500);
|
|
126
|
+
--color-chart-5-accent: theme(colors.red.300);
|
|
127
|
+
--color-chart-5-foreground: theme(colors.red.100);
|
|
128
|
+
--color-chart-6: theme(colors.gray.500);
|
|
129
|
+
--color-chart-6-accent: theme(colors.gray.300);
|
|
130
|
+
--color-chart-6-foreground: theme(colors.gray.100);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/* ---------------------------------------------------------------------------
|
|
134
|
+
Thème sombre.
|
|
135
|
+
|
|
136
|
+
Hors `@theme` et donc hors couche : le sélecteur d'attribut l'emporte à la
|
|
137
|
+
fois par spécificité et par la règle des couches, ce qui rend la bascule
|
|
138
|
+
déterministe.
|
|
139
|
+
|
|
140
|
+
Toutes les paires de texte sont vérifiées au ratio WCAG AA (4.5:1). Les plus
|
|
141
|
+
serrées sont ici plus confortables qu'en thème clair, où `primary` est à
|
|
142
|
+
exactement 4.50.
|
|
143
|
+
|
|
144
|
+
Les échelles numériques (`--color-primary-50` à `900`, `--color-chart-*`) ne
|
|
145
|
+
sont PAS inversées : ce sont des gammes, choisies délibérément par une
|
|
146
|
+
application, pas des rôles. Les usages qui avaient besoin de suivre le thème
|
|
147
|
+
sont passés aux tokens sémantiques ci-dessous.
|
|
148
|
+
--------------------------------------------------------------------------- */
|
|
149
|
+
:root[data-theme='dark'] {
|
|
150
|
+
/* Surfaces, du plus sombre au plus clair : le fond de page porte les cartes,
|
|
151
|
+
les cartes portent les panneaux flottants. En sombre l'ombre portée ne
|
|
152
|
+
sépare presque rien — c'est la valeur de fond qui fait ce travail. */
|
|
153
|
+
--color-background: #020617; /* slate-950 */
|
|
154
|
+
--color-card: #0f172a; /* slate-900 */
|
|
155
|
+
--color-popover: #1e293b; /* slate-800 */
|
|
156
|
+
|
|
157
|
+
--color-foreground: #f8fafc; /* slate-50 */
|
|
158
|
+
--color-card-foreground: #f8fafc;
|
|
159
|
+
--color-popover-foreground: #f8fafc;
|
|
160
|
+
--color-muted: #1e293b; /* slate-800 */
|
|
161
|
+
--color-muted-foreground: #94a3b8; /* slate-400 */
|
|
162
|
+
--color-subtitle: #94a3b8;
|
|
163
|
+
--color-border-color: #334155; /* slate-700 */
|
|
164
|
+
--color-input: #334155;
|
|
165
|
+
--color-ring: #cbd5e1; /* slate-300 */
|
|
166
|
+
--color-disabled: #1e293b; /* slate-800 */
|
|
167
|
+
/* slate-800 et non slate-700 : les onglets non sélectionnés posent `text-subtitle`
|
|
168
|
+
dessus, et sur slate-700 la paire tombe à 4.04, sous le seuil AA de 4.5. */
|
|
169
|
+
--color-track: #1e293b; /* slate-800 */
|
|
170
|
+
--color-elevated: #334155; /* slate-700 : un cran AU-DESSUS de track */
|
|
171
|
+
--color-scrim: #020617; /* slate-950 : le voile assombrit, il ne s'inverse pas */
|
|
172
|
+
|
|
173
|
+
--color-primary: #32fe6b; /* primary-400 : #008a26 échoue au contraste sur fond sombre */
|
|
174
|
+
--color-primary-hover: #6fff9a; /* primary-300 : le survol ÉCLAIRCIT sur fond clair */
|
|
175
|
+
--color-primary-foreground: #020617;
|
|
176
|
+
--color-primary-subtle: #052e16;
|
|
177
|
+
--color-primary-subtle-foreground: #32fe6b;
|
|
178
|
+
|
|
179
|
+
--color-info: #60a5fa; /* blue-400 */
|
|
180
|
+
--color-info-hover: #93c5fd; /* blue-300 */
|
|
181
|
+
--color-info-foreground: #172554; /* blue-950 */
|
|
182
|
+
--color-success: #4ade80; /* green-400 */
|
|
183
|
+
--color-success-foreground: #052e16; /* green-950 */
|
|
184
|
+
--color-destructive: #f87171; /* red-400 */
|
|
185
|
+
--color-destructive-hover: #fca5a5; /* red-300 */
|
|
186
|
+
--color-destructive-foreground: #450a0a; /* red-950 */
|
|
187
|
+
--color-warning: #fb923c; /* orange-400 */
|
|
188
|
+
--color-warning-hover: #fdba74; /* orange-300 */
|
|
189
|
+
--color-warning-foreground: #431407; /* orange-950 */
|
|
190
|
+
--color-neutral: #cbd5e1; /* slate-300 */
|
|
191
|
+
--color-neutral-foreground: #020617;
|
|
192
|
+
|
|
193
|
+
/* L'infobulle s'inverse : sombre sur clair, elle ne se détacherait plus d'une
|
|
194
|
+
interface déjà sombre. */
|
|
195
|
+
--color-tooltip-surface: #e2e8f0; /* slate-200 */
|
|
196
|
+
--color-tooltip-border: #94a3b8; /* slate-400 */
|
|
197
|
+
--color-tooltip-foreground: #020617;
|
|
198
|
+
--color-tooltip-subtitle: #334155; /* slate-700 */
|
|
199
|
+
}
|
package/utilities.css
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/* ---------------------------------------------------------------------------
|
|
2
|
+
Utilitaires typographiques du design system SkillCorner.
|
|
3
|
+
|
|
4
|
+
Exige `tokens.css` : `font-title` et `text-subtitle` viennent de là.
|
|
5
|
+
|
|
6
|
+
`title-three` est en `font-semibold`, l'usage d'orion ; gaffer rendait en
|
|
7
|
+
`font-medium` et s'aligne.
|
|
8
|
+
`info-caption` venait de gaffer seul : générique et additif, il monte ici.
|
|
9
|
+
--------------------------------------------------------------------------- */
|
|
10
|
+
|
|
11
|
+
@utility text-body-large {
|
|
12
|
+
@apply text-lg leading-7;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
@utility text-body-medium {
|
|
16
|
+
@apply text-base leading-6;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
@utility text-body-small {
|
|
20
|
+
@apply text-sm leading-5;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@utility text-caption {
|
|
24
|
+
@apply text-xs leading-4;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@utility info-caption {
|
|
28
|
+
@apply text-caption italic text-subtitle;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@utility title-one {
|
|
32
|
+
@apply font-bold text-6xl leading-10 font-title;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@utility title-two {
|
|
36
|
+
@apply font-semibold text-5xl leading-12 font-title;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@utility title-three {
|
|
40
|
+
@apply font-semibold text-4xl leading-10 font-title;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@utility title-four {
|
|
44
|
+
@apply font-medium text-3xl leading-8 font-title;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@utility title-five {
|
|
48
|
+
@apply font-medium text-2xl leading-7 font-title;
|
|
49
|
+
}
|