@jjlmoya/utils-developer 1.2.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.
Files changed (228) hide show
  1. package/package.json +64 -0
  2. package/src/category/i18n/en.ts +34 -0
  3. package/src/category/i18n/es.ts +34 -0
  4. package/src/category/i18n/fr.ts +34 -0
  5. package/src/category/index.ts +31 -0
  6. package/src/category/seo.astro +15 -0
  7. package/src/components/PreviewNavSidebar.astro +116 -0
  8. package/src/components/PreviewToolbar.astro +143 -0
  9. package/src/data.ts +11 -0
  10. package/src/env.d.ts +5 -0
  11. package/src/index.ts +78 -0
  12. package/src/layouts/PreviewLayout.astro +117 -0
  13. package/src/pages/[locale]/[slug].astro +146 -0
  14. package/src/pages/[locale].astro +251 -0
  15. package/src/pages/index.astro +4 -0
  16. package/src/tests/css_specificity_logic.test.ts +39 -0
  17. package/src/tests/faq_count.test.ts +19 -0
  18. package/src/tests/locale_completeness.test.ts +42 -0
  19. package/src/tests/mocks/astro_mock.js +2 -0
  20. package/src/tests/no_h1_in_components.test.ts +48 -0
  21. package/src/tests/schemas_fulfillment.test.ts +23 -0
  22. package/src/tests/seo_length.test.ts +22 -0
  23. package/src/tests/title_quality.test.ts +55 -0
  24. package/src/tests/tool_validation.test.ts +17 -0
  25. package/src/tool/aspectRatio/bibliography.astro +14 -0
  26. package/src/tool/aspectRatio/component.astro +468 -0
  27. package/src/tool/aspectRatio/i18n/en.ts +177 -0
  28. package/src/tool/aspectRatio/i18n/es.ts +177 -0
  29. package/src/tool/aspectRatio/i18n/fr.ts +177 -0
  30. package/src/tool/aspectRatio/index.ts +34 -0
  31. package/src/tool/aspectRatio/seo.astro +14 -0
  32. package/src/tool/aspectRatio/ui.ts +12 -0
  33. package/src/tool/calculadoraTiempoDatos/bibliography.astro +14 -0
  34. package/src/tool/calculadoraTiempoDatos/component.astro +829 -0
  35. package/src/tool/calculadoraTiempoDatos/i18n/en.ts +150 -0
  36. package/src/tool/calculadoraTiempoDatos/i18n/es.ts +163 -0
  37. package/src/tool/calculadoraTiempoDatos/i18n/fr.ts +150 -0
  38. package/src/tool/calculadoraTiempoDatos/index.ts +33 -0
  39. package/src/tool/calculadoraTiempoDatos/seo.astro +14 -0
  40. package/src/tool/calculadoraTiempoDatos/ui.ts +24 -0
  41. package/src/tool/colorConverter/bibliography.astro +14 -0
  42. package/src/tool/colorConverter/component.astro +504 -0
  43. package/src/tool/colorConverter/i18n/en.ts +126 -0
  44. package/src/tool/colorConverter/i18n/es.ts +126 -0
  45. package/src/tool/colorConverter/i18n/fr.ts +126 -0
  46. package/src/tool/colorConverter/index.ts +33 -0
  47. package/src/tool/colorConverter/seo.astro +14 -0
  48. package/src/tool/colorConverter/ui.ts +16 -0
  49. package/src/tool/conversorExcelCsvHtml/bibliography.astro +14 -0
  50. package/src/tool/conversorExcelCsvHtml/component.astro +595 -0
  51. package/src/tool/conversorExcelCsvHtml/i18n/en.ts +129 -0
  52. package/src/tool/conversorExcelCsvHtml/i18n/es.ts +129 -0
  53. package/src/tool/conversorExcelCsvHtml/i18n/fr.ts +129 -0
  54. package/src/tool/conversorExcelCsvHtml/index.ts +33 -0
  55. package/src/tool/conversorExcelCsvHtml/seo.astro +14 -0
  56. package/src/tool/conversorExcelCsvHtml/ui.ts +14 -0
  57. package/src/tool/cronGenerator/bibliography.astro +14 -0
  58. package/src/tool/cronGenerator/component.astro +667 -0
  59. package/src/tool/cronGenerator/i18n/en.ts +191 -0
  60. package/src/tool/cronGenerator/i18n/es.ts +191 -0
  61. package/src/tool/cronGenerator/i18n/fr.ts +191 -0
  62. package/src/tool/cronGenerator/index.ts +34 -0
  63. package/src/tool/cronGenerator/seo.astro +14 -0
  64. package/src/tool/cronGenerator/ui.ts +45 -0
  65. package/src/tool/cssSpecificityCalculator/bibliography.astro +14 -0
  66. package/src/tool/cssSpecificityCalculator/component.astro +292 -0
  67. package/src/tool/cssSpecificityCalculator/i18n/en.ts +168 -0
  68. package/src/tool/cssSpecificityCalculator/i18n/es.ts +168 -0
  69. package/src/tool/cssSpecificityCalculator/i18n/fr.ts +168 -0
  70. package/src/tool/cssSpecificityCalculator/index.ts +34 -0
  71. package/src/tool/cssSpecificityCalculator/logic.ts +100 -0
  72. package/src/tool/cssSpecificityCalculator/seo.astro +14 -0
  73. package/src/tool/cssSpecificityCalculator/ui-controller.ts +57 -0
  74. package/src/tool/cssSpecificityCalculator/ui.ts +11 -0
  75. package/src/tool/cssToInlineConverter/bibliography.astro +14 -0
  76. package/src/tool/cssToInlineConverter/component.astro +308 -0
  77. package/src/tool/cssToInlineConverter/i18n/en.ts +160 -0
  78. package/src/tool/cssToInlineConverter/i18n/es.ts +160 -0
  79. package/src/tool/cssToInlineConverter/i18n/fr.ts +160 -0
  80. package/src/tool/cssToInlineConverter/index.ts +34 -0
  81. package/src/tool/cssToInlineConverter/seo.astro +14 -0
  82. package/src/tool/cssToInlineConverter/ui.ts +12 -0
  83. package/src/tool/duplicateCssRemover/bibliography.astro +14 -0
  84. package/src/tool/duplicateCssRemover/component.astro +353 -0
  85. package/src/tool/duplicateCssRemover/i18n/en.ts +167 -0
  86. package/src/tool/duplicateCssRemover/i18n/es.ts +167 -0
  87. package/src/tool/duplicateCssRemover/i18n/fr.ts +167 -0
  88. package/src/tool/duplicateCssRemover/index.ts +34 -0
  89. package/src/tool/duplicateCssRemover/seo.astro +14 -0
  90. package/src/tool/duplicateCssRemover/ui.ts +12 -0
  91. package/src/tool/generadorSecurityTxt/bibliography.astro +14 -0
  92. package/src/tool/generadorSecurityTxt/component.astro +473 -0
  93. package/src/tool/generadorSecurityTxt/i18n/en.ts +173 -0
  94. package/src/tool/generadorSecurityTxt/i18n/es.ts +173 -0
  95. package/src/tool/generadorSecurityTxt/i18n/fr.ts +173 -0
  96. package/src/tool/generadorSecurityTxt/index.ts +33 -0
  97. package/src/tool/generadorSecurityTxt/seo.astro +14 -0
  98. package/src/tool/generadorSecurityTxt/ui.ts +27 -0
  99. package/src/tool/hashGenerator/bibliography.astro +14 -0
  100. package/src/tool/hashGenerator/component.astro +367 -0
  101. package/src/tool/hashGenerator/i18n/en.ts +118 -0
  102. package/src/tool/hashGenerator/i18n/es.ts +118 -0
  103. package/src/tool/hashGenerator/i18n/fr.ts +118 -0
  104. package/src/tool/hashGenerator/index.ts +33 -0
  105. package/src/tool/hashGenerator/md5.ts +138 -0
  106. package/src/tool/hashGenerator/seo.astro +14 -0
  107. package/src/tool/hashGenerator/ui.ts +11 -0
  108. package/src/tool/inspectorCertificadosSsl/bibliography.astro +14 -0
  109. package/src/tool/inspectorCertificadosSsl/component.astro +580 -0
  110. package/src/tool/inspectorCertificadosSsl/i18n/en.ts +214 -0
  111. package/src/tool/inspectorCertificadosSsl/i18n/es.ts +214 -0
  112. package/src/tool/inspectorCertificadosSsl/i18n/fr.ts +214 -0
  113. package/src/tool/inspectorCertificadosSsl/index.ts +33 -0
  114. package/src/tool/inspectorCertificadosSsl/seo.astro +14 -0
  115. package/src/tool/inspectorCertificadosSsl/ui.ts +29 -0
  116. package/src/tool/jsonFormatter/bibliography.astro +14 -0
  117. package/src/tool/jsonFormatter/component.astro +590 -0
  118. package/src/tool/jsonFormatter/i18n/en.ts +156 -0
  119. package/src/tool/jsonFormatter/i18n/es.ts +156 -0
  120. package/src/tool/jsonFormatter/i18n/fr.ts +156 -0
  121. package/src/tool/jsonFormatter/index.ts +34 -0
  122. package/src/tool/jsonFormatter/seo.astro +14 -0
  123. package/src/tool/jsonFormatter/ui.ts +15 -0
  124. package/src/tool/keycode/bibliography.astro +14 -0
  125. package/src/tool/keycode/component.astro +540 -0
  126. package/src/tool/keycode/i18n/en.ts +160 -0
  127. package/src/tool/keycode/i18n/es.ts +160 -0
  128. package/src/tool/keycode/i18n/fr.ts +160 -0
  129. package/src/tool/keycode/index.ts +34 -0
  130. package/src/tool/keycode/seo.astro +14 -0
  131. package/src/tool/keycode/ui.ts +10 -0
  132. package/src/tool/llmCostCalculator/bibliography.astro +14 -0
  133. package/src/tool/llmCostCalculator/component.astro +488 -0
  134. package/src/tool/llmCostCalculator/data.ts +99 -0
  135. package/src/tool/llmCostCalculator/i18n/en.ts +165 -0
  136. package/src/tool/llmCostCalculator/i18n/es.ts +165 -0
  137. package/src/tool/llmCostCalculator/i18n/fr.ts +165 -0
  138. package/src/tool/llmCostCalculator/index.ts +34 -0
  139. package/src/tool/llmCostCalculator/seo.astro +14 -0
  140. package/src/tool/llmCostCalculator/ui.ts +11 -0
  141. package/src/tool/mobileMockupGenerator/app.ts +614 -0
  142. package/src/tool/mobileMockupGenerator/bibliography.astro +14 -0
  143. package/src/tool/mobileMockupGenerator/component.astro +1044 -0
  144. package/src/tool/mobileMockupGenerator/constants.ts +48 -0
  145. package/src/tool/mobileMockupGenerator/i18n/en.ts +121 -0
  146. package/src/tool/mobileMockupGenerator/i18n/es.ts +121 -0
  147. package/src/tool/mobileMockupGenerator/i18n/fr.ts +121 -0
  148. package/src/tool/mobileMockupGenerator/index.ts +33 -0
  149. package/src/tool/mobileMockupGenerator/rendering.ts +189 -0
  150. package/src/tool/mobileMockupGenerator/seo.astro +14 -0
  151. package/src/tool/mobileMockupGenerator/store.ts +145 -0
  152. package/src/tool/mobileMockupGenerator/types.ts +41 -0
  153. package/src/tool/mobileMockupGenerator/ui.ts +48 -0
  154. package/src/tool/musicalTypography/bibliography.astro +14 -0
  155. package/src/tool/musicalTypography/component.astro +480 -0
  156. package/src/tool/musicalTypography/i18n/en.ts +179 -0
  157. package/src/tool/musicalTypography/i18n/es.ts +179 -0
  158. package/src/tool/musicalTypography/i18n/fr.ts +179 -0
  159. package/src/tool/musicalTypography/index.ts +33 -0
  160. package/src/tool/musicalTypography/seo.astro +14 -0
  161. package/src/tool/musicalTypography/ui.ts +25 -0
  162. package/src/tool/placeholderGenerator/bibliography.astro +14 -0
  163. package/src/tool/placeholderGenerator/component.astro +499 -0
  164. package/src/tool/placeholderGenerator/i18n/en.ts +176 -0
  165. package/src/tool/placeholderGenerator/i18n/es.ts +176 -0
  166. package/src/tool/placeholderGenerator/i18n/fr.ts +176 -0
  167. package/src/tool/placeholderGenerator/index.ts +34 -0
  168. package/src/tool/placeholderGenerator/seo.astro +14 -0
  169. package/src/tool/placeholderGenerator/ui.ts +14 -0
  170. package/src/tool/promptLibrary/bibliography.astro +14 -0
  171. package/src/tool/promptLibrary/component.astro +1131 -0
  172. package/src/tool/promptLibrary/controller.ts +49 -0
  173. package/src/tool/promptLibrary/i18n/en.ts +131 -0
  174. package/src/tool/promptLibrary/i18n/es.ts +131 -0
  175. package/src/tool/promptLibrary/i18n/fr.ts +126 -0
  176. package/src/tool/promptLibrary/index.ts +33 -0
  177. package/src/tool/promptLibrary/seo.astro +14 -0
  178. package/src/tool/promptLibrary/ui.ts +29 -0
  179. package/src/tool/readabilityCalculator/bibliography.astro +14 -0
  180. package/src/tool/readabilityCalculator/component.astro +534 -0
  181. package/src/tool/readabilityCalculator/i18n/en.ts +133 -0
  182. package/src/tool/readabilityCalculator/i18n/es.ts +133 -0
  183. package/src/tool/readabilityCalculator/i18n/fr.ts +133 -0
  184. package/src/tool/readabilityCalculator/index.ts +33 -0
  185. package/src/tool/readabilityCalculator/seo.astro +14 -0
  186. package/src/tool/readabilityCalculator/ui.ts +27 -0
  187. package/src/tool/svgSanitizer/bibliography.astro +14 -0
  188. package/src/tool/svgSanitizer/component.astro +554 -0
  189. package/src/tool/svgSanitizer/i18n/en.ts +125 -0
  190. package/src/tool/svgSanitizer/i18n/es.ts +125 -0
  191. package/src/tool/svgSanitizer/i18n/fr.ts +125 -0
  192. package/src/tool/svgSanitizer/index.ts +33 -0
  193. package/src/tool/svgSanitizer/seo.astro +14 -0
  194. package/src/tool/svgSanitizer/ui.ts +18 -0
  195. package/src/tool/svgToCss/bibliography.astro +14 -0
  196. package/src/tool/svgToCss/component.astro +381 -0
  197. package/src/tool/svgToCss/i18n/en.ts +174 -0
  198. package/src/tool/svgToCss/i18n/es.ts +174 -0
  199. package/src/tool/svgToCss/i18n/fr.ts +174 -0
  200. package/src/tool/svgToCss/index.ts +34 -0
  201. package/src/tool/svgToCss/seo.astro +14 -0
  202. package/src/tool/svgToCss/ui.ts +13 -0
  203. package/src/tool/urlCleaner/bibliography.astro +14 -0
  204. package/src/tool/urlCleaner/component.astro +458 -0
  205. package/src/tool/urlCleaner/i18n/en.ts +116 -0
  206. package/src/tool/urlCleaner/i18n/es.ts +116 -0
  207. package/src/tool/urlCleaner/i18n/fr.ts +116 -0
  208. package/src/tool/urlCleaner/index.ts +33 -0
  209. package/src/tool/urlCleaner/seo.astro +14 -0
  210. package/src/tool/urlCleaner/ui.ts +12 -0
  211. package/src/tool/urlEncoderDecoder/bibliography.astro +14 -0
  212. package/src/tool/urlEncoderDecoder/component.astro +291 -0
  213. package/src/tool/urlEncoderDecoder/i18n/en.ts +162 -0
  214. package/src/tool/urlEncoderDecoder/i18n/es.ts +162 -0
  215. package/src/tool/urlEncoderDecoder/i18n/fr.ts +162 -0
  216. package/src/tool/urlEncoderDecoder/index.ts +34 -0
  217. package/src/tool/urlEncoderDecoder/seo.astro +14 -0
  218. package/src/tool/urlEncoderDecoder/ui.ts +11 -0
  219. package/src/tool/utmGenerator/bibliography.astro +14 -0
  220. package/src/tool/utmGenerator/component.astro +270 -0
  221. package/src/tool/utmGenerator/i18n/en.ts +117 -0
  222. package/src/tool/utmGenerator/i18n/es.ts +117 -0
  223. package/src/tool/utmGenerator/i18n/fr.ts +117 -0
  224. package/src/tool/utmGenerator/index.ts +33 -0
  225. package/src/tool/utmGenerator/seo.astro +14 -0
  226. package/src/tool/utmGenerator/ui.ts +13 -0
  227. package/src/tools.ts +27 -0
  228. package/src/types.ts +72 -0
@@ -0,0 +1,168 @@
1
+ import type { WithContext, FAQPage, HowToThing, SoftwareApplication } from 'schema-dts';
2
+ import type { ToolLocaleContent } from '../../../types';
3
+ import type { CssSpecificityCalculatorUI } from '../ui';
4
+
5
+ const slug = 'calculateur-specificite-css';
6
+ const title = 'Calculateur de Spécificité CSS en Ligne. Visualiseur de Poids';
7
+ const description =
8
+ 'Calculez la spécificité et le poids exact de n\'importe quel sélecteur CSS. Outil visuel pour comprendre quelle règle CSS gagne la cascade et éviter l\'utilisation de !important.';
9
+
10
+ const faqData = [
11
+ {
12
+ question: 'Qu\'est-ce que la spécificité CSS ?',
13
+ answer:
14
+ 'La spécificité est l\'algorithme que les navigateurs utilisent pour décider quelle règle CSS s\'applique à un élément quand plusieurs règles sont en compétition. Elle est représentée par un score à trois colonnes (A, B, C) indiquant les IDs, les classes/attributs/pseudo-classes et les éléments/pseudo-éléments.',
15
+ },
16
+ {
17
+ question: 'Une classe peut-elle jamais battre un ID en spécificité ?',
18
+ answer:
19
+ 'Pas directement. Un ID (1,0,0) bat toujours n\'importe quel nombre de classes (0,N,0) car la spécificité n\'a pas de retenue entre les colonnes. Cent classes (0,100,0) ne battront jamais un seul ID (1,0,0).',
20
+ },
21
+ {
22
+ question: 'Que se passe-t-il quand deux sélecteurs ont la même spécificité ?',
23
+ answer:
24
+ 'Quand deux sélecteurs ont exactement le même poids, celui déclaré en dernier dans le fichier CSS gagne. C\'est ce qu\'on appelle l\'ordre naturel de la cascade. Cette calculatrice vous avertit visuellement quand une égalité se produit.',
25
+ },
26
+ {
27
+ question: 'Pourquoi l\'utilisation de !important est-elle considérée comme mauvaise pratique ?',
28
+ answer:
29
+ 'La directive !important brise la cascade CSS naturelle en forçant une déclaration sur toute autre règle. Cela crée des conflits difficiles à déboguer dans les grands projets. Des méthodologies comme BEM préconisent de garder une spécificité plate pour éviter d\'avoir besoin de !important.',
30
+ },
31
+ ];
32
+
33
+ const howToData = [
34
+ {
35
+ name: 'Saisissez le premier sélecteur',
36
+ text: 'Tapez le Sélecteur A dans le champ texte de gauche, par exemple : #header .nav-item > a. Les compteurs d\'IDs, Classes et Éléments se mettront à jour instantanément.',
37
+ },
38
+ {
39
+ name: 'Saisissez le deuxième sélecteur',
40
+ text: 'Tapez le Sélecteur B dans le champ texte de droite, par exemple : ul li.active a:hover. Observez comment les poids changent en temps réel.',
41
+ },
42
+ {
43
+ name: 'Lisez le résultat',
44
+ text: 'La calculatrice mettra en évidence le bloc du sélecteur gagnant avec une bannière verte. En cas d\'égalité, un message apparaîtra expliquant l\'ordre de cascade comme critère de départage.',
45
+ },
46
+ ];
47
+
48
+ const faqSchema: WithContext<FAQPage> = {
49
+ '@context': 'https://schema.org',
50
+ '@type': 'FAQPage',
51
+ mainEntity: faqData.map((item) => ({
52
+ '@type': 'Question',
53
+ name: item.question,
54
+ acceptedAnswer: { '@type': 'Answer', text: item.answer },
55
+ })),
56
+ };
57
+
58
+ const howToSchema: WithContext<HowToThing> = {
59
+ '@context': 'https://schema.org',
60
+ '@type': 'HowTo',
61
+ name: title,
62
+ description,
63
+ step: howToData.map((step, i) => ({
64
+ '@type': 'HowToStep',
65
+ position: i + 1,
66
+ name: step.name,
67
+ text: step.text,
68
+ })),
69
+ };
70
+
71
+ const appSchema: WithContext<SoftwareApplication> = {
72
+ '@context': 'https://schema.org',
73
+ '@type': 'SoftwareApplication',
74
+ name: title,
75
+ description,
76
+ applicationCategory: 'UtilityApplication',
77
+ operatingSystem: 'All',
78
+ offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' },
79
+ inLanguage: 'fr',
80
+ };
81
+
82
+ const ui: CssSpecificityCalculatorUI = {
83
+ labelA: 'Sélecteur A',
84
+ labelB: 'Sélecteur B',
85
+ placeholderA: 'ex. #header .nav-item > a',
86
+ placeholderB: 'ex. ul li.active a:hover',
87
+ cardIds: 'IDs',
88
+ cardClasses: 'Classes / Pseudos',
89
+ cardElements: 'Éléments',
90
+ bannerWinner: 'Ce sélecteur gagne !',
91
+ msgTie: 'Les deux sélecteurs ont le même poids. S\'ils sont en compétition pour le même élément, celui écrit en <strong>dernier</strong> dans le CSS gagne.',
92
+ };
93
+
94
+ export const content: ToolLocaleContent<CssSpecificityCalculatorUI> = {
95
+ slug,
96
+ title,
97
+ description,
98
+ ui,
99
+ faqTitle: 'Questions Fréquentes',
100
+ faq: faqData,
101
+ bibliographyTitle: 'Références et Documentation',
102
+ bibliography: [
103
+ {
104
+ name: 'MDN Web Docs: Spécificité CSS',
105
+ url: 'https://developer.mozilla.org/fr/docs/Web/CSS/Specificity',
106
+ },
107
+ {
108
+ name: 'W3C: Selectors Level 3 - Spécificité',
109
+ url: 'https://www.w3.org/TR/selectors-3/#specificity',
110
+ },
111
+ ],
112
+ howTo: howToData,
113
+ schemas: [faqSchema, howToSchema, appSchema],
114
+ seo: [
115
+ {
116
+ type: 'title',
117
+ text: 'Qu\'est-ce que la Spécificité CSS ?',
118
+ level: 2,
119
+ },
120
+ {
121
+ type: 'paragraph',
122
+ html: 'La spécificité CSS est l\'algorithme par lequel les navigateurs décident quelles valeurs de propriétés appliquer à un élément particulier. C\'est essentiellement un score mathématique qui indique au navigateur "à quel point" une règle est spécifique.',
123
+ },
124
+ {
125
+ type: 'paragraph',
126
+ html: 'Si deux règles ont des niveaux de spécificité différents, celle avec le poids le plus élevé gagnera, quel que soit l\'ordre dans lequel elles ont été écrites. Si les deux ont le même poids, la dernière déclarée dans le code source gagne.',
127
+ },
128
+ {
129
+ type: 'title',
130
+ text: 'Comment calculer la Spécificité CSS',
131
+ level: 3,
132
+ },
133
+ {
134
+ type: 'paragraph',
135
+ html: 'La spécificité est calculée sur trois catégories formant un poids à trois colonnes, exprimé comme <strong>(A, B, C)</strong> :',
136
+ },
137
+ {
138
+ type: 'list',
139
+ items: [
140
+ '<strong>Colonne A (IDs) :</strong> Compte le nombre d\'identifiants uniques. Exemple : <code>#header</code> compte comme 1 dans la colonne A.',
141
+ '<strong>Colonne B (Classes, Attributs et Pseudo-classes) :</strong> Compte toutes les classes (<code>.button</code>), attributs (<code>[type="text"]</code>) et pseudo-classes (<code>:hover</code>).',
142
+ '<strong>Colonne C (Éléments et Pseudo-éléments) :</strong> Compte tous les éléments HTML (<code>div</code>, <code>h1</code>) et pseudo-éléments (<code>::before</code>).',
143
+ ],
144
+ },
145
+ {
146
+ type: 'title',
147
+ text: 'La règle d\'or: pas de report numérique',
148
+ level: 3,
149
+ },
150
+ {
151
+ type: 'tip',
152
+ html: 'Une règle avec la spécificité (0,50,0) ne sera <strong>jamais</strong> plus spécifique qu\'une règle (1,0,0). <strong>Un seul ID bat infiniment de classes.</strong> Les colonnes ne se déversent jamais les unes dans les autres.',
153
+ },
154
+ {
155
+ type: 'title',
156
+ text: 'Le problème de !important et l\'architecture BEM',
157
+ level: 3,
158
+ },
159
+ {
160
+ type: 'paragraph',
161
+ html: 'La directive <code>!important</code> est une exception aux règles de spécificité. Quand elle est utilisée, cette déclaration écrase automatiquement toute autre. Elle est considérée comme une mauvaise pratique car elle détruit la cascade naturelle.',
162
+ },
163
+ {
164
+ type: 'paragraph',
165
+ html: 'Pour éviter les guerres de spécificité dans les grands projets, des méthodologies comme <strong>BEM</strong> préconisent d\'utiliser uniquement des sélecteurs de classe à un seul niveau de profondeur, maintenant artificiellement la spécificité plate à (0,1,0).',
166
+ },
167
+ ],
168
+ };
@@ -0,0 +1,34 @@
1
+ import type { DeveloperToolEntry, ToolLocaleContent, ToolDefinition } from '../../types';
2
+ import CssSpecificityCalculatorComponent from './component.astro';
3
+ import CssSpecificityCalculatorSEO from './seo.astro';
4
+ import CssSpecificityCalculatorBibliography from './bibliography.astro';
5
+
6
+ import type { CssSpecificityCalculatorUI } from './ui';
7
+
8
+ export type CssSpecificityCalculatorLocaleContent = ToolLocaleContent<CssSpecificityCalculatorUI>;
9
+
10
+ import { content as es } from './i18n/es';
11
+ import { content as en } from './i18n/en';
12
+ import { content as fr } from './i18n/fr';
13
+
14
+ export const cssSpecificityCalculator: DeveloperToolEntry<CssSpecificityCalculatorUI> = {
15
+ id: 'css-specificity-calculator',
16
+ icons: {
17
+ bg: 'mdi:scale-balance',
18
+ fg: 'mdi:language-css3',
19
+ },
20
+ i18n: {
21
+ es: async () => es,
22
+ en: async () => en,
23
+ fr: async () => fr,
24
+ },
25
+ };
26
+
27
+ export { CssSpecificityCalculatorComponent, CssSpecificityCalculatorSEO, CssSpecificityCalculatorBibliography };
28
+
29
+ export const CSS_SPECIFICITY_CALCULATOR_TOOL: ToolDefinition = {
30
+ entry: cssSpecificityCalculator,
31
+ Component: CssSpecificityCalculatorComponent,
32
+ SEOComponent: CssSpecificityCalculatorSEO,
33
+ BibliographyComponent: CssSpecificityCalculatorBibliography,
34
+ };
@@ -0,0 +1,100 @@
1
+ export type Specificity = [number, number, number];
2
+
3
+ export function compareSpecificity(a: Specificity, b: Specificity): number {
4
+ for (let i = 0; i < 3; i++) {
5
+ if (a[i] > b[i]) return 1;
6
+ if (a[i] < b[i]) return -1;
7
+ }
8
+ return 0;
9
+ }
10
+
11
+ function getFunctionalPseudoContent(s: string, start: number): { content: string; endIndex: number } | null {
12
+ let level = 1;
13
+ let i = start;
14
+ while (i < s.length && level > 0) {
15
+ if (s[i] === '(') level++;
16
+ else if (s[i] === ')') level--;
17
+ i++;
18
+ }
19
+ if (level === 0) return { content: s.substring(start, i - 1), endIndex: i };
20
+ return null;
21
+ }
22
+
23
+ function splitByComma(s: string): string[] {
24
+ const result: string[] = [];
25
+ let current = '';
26
+ let level = 0;
27
+ for (let i = 0; i < s.length; i++) {
28
+ if (s[i] === '(') level++;
29
+ else if (s[i] === ')') level--;
30
+ if (s[i] === ',' && level === 0) {
31
+ result.push(current.trim());
32
+ current = '';
33
+ } else {
34
+ current += s[i];
35
+ }
36
+ }
37
+ if (current.trim()) result.push(current.trim());
38
+ return result;
39
+ }
40
+
41
+ function addSubSpecificity(content: string, total: Specificity): void {
42
+ const subs = splitByComma(content);
43
+ let max: Specificity = [0, 0, 0];
44
+ for (const sub of subs) {
45
+ const s = calculateSpecificity(sub);
46
+ if (compareSpecificity(s, max) > 0) max = s;
47
+ }
48
+ total[0] += max[0];
49
+ total[1] += max[1];
50
+ total[2] += max[2];
51
+ }
52
+
53
+ function processPseudo(s: string, pseudo: string, total: Specificity): string | null {
54
+ const idx = s.indexOf(pseudo);
55
+ if (idx === -1) return null;
56
+ const res = getFunctionalPseudoContent(s, idx + pseudo.length);
57
+ if (!res) return null;
58
+ if (pseudo !== ':where(') addSubSpecificity(res.content, total);
59
+ return s.substring(0, idx) + ' ' + s.substring(res.endIndex);
60
+ }
61
+
62
+ function applyFunctionalPseudos(s: string, total: Specificity): string {
63
+ const pseudos = [':is(', ':not(', ':where(', ':has('];
64
+ let found = true;
65
+ while (found) {
66
+ found = false;
67
+ for (const pseudo of pseudos) {
68
+ const result = processPseudo(s, pseudo, total);
69
+ if (result !== null) { s = result; found = true; }
70
+ }
71
+ }
72
+ return s;
73
+ }
74
+
75
+ function countMatches(s: string, regex: RegExp): number {
76
+ return (s.match(regex) ?? []).length;
77
+ }
78
+
79
+ function countParts(s: string): number {
80
+ const parts = s.replace(/[*+>~,]/g, ' ').trim().split(/\s+/);
81
+ return parts[0] ? parts.length : 0;
82
+ }
83
+
84
+ export function calculateSpecificity(selector: string): Specificity {
85
+ if (!selector.trim()) return [0, 0, 0];
86
+ const total: Specificity = [0, 0, 0];
87
+ let s = applyFunctionalPseudos(selector, total);
88
+ total[0] += countMatches(s, /#[\w-]+/g);
89
+ s = s.replace(/#[\w-]+/g, ' ');
90
+ total[1] += countMatches(s, /\.[\w-]+/g);
91
+ s = s.replace(/\.[\w-]+/g, ' ');
92
+ total[1] += countMatches(s, /\[[^\]]+\]/g);
93
+ s = s.replace(/\[[^\]]+\]/g, ' ');
94
+ total[2] += countMatches(s, /::[\w-]+/g);
95
+ s = s.replace(/::[\w-]+/g, ' ');
96
+ total[1] += countMatches(s, /:[\w-]+(\([^)]*\))?/g);
97
+ s = s.replace(/:[\w-]+(\([^)]*\))?/g, ' ');
98
+ total[2] += countParts(s);
99
+ return total;
100
+ }
@@ -0,0 +1,14 @@
1
+ ---
2
+ import { SEORenderer } from '@jjlmoya/utils-shared';
3
+ import { cssSpecificityCalculator } from './index';
4
+ import type { KnownLocale } from '../../types';
5
+
6
+ interface Props {
7
+ locale?: KnownLocale;
8
+ }
9
+
10
+ const { locale = 'es' } = Astro.props;
11
+ const content = await cssSpecificityCalculator.i18n[locale]?.();
12
+ ---
13
+
14
+ {content && <SEORenderer content={{ locale, sections: content.seo }} />}
@@ -0,0 +1,57 @@
1
+ import { calculateSpecificity, compareSpecificity } from './logic';
2
+
3
+ type SpecDisp = { ids: HTMLElement; cls: HTMLElement; els: HTMLElement };
4
+ type SpecBlocks = { a: HTMLElement; b: HTMLElement; tie: HTMLElement };
5
+
6
+ function renderSpec(disp: SpecDisp, spec: [number, number, number]): void {
7
+ disp.ids.textContent = spec[0].toString();
8
+ disp.cls.textContent = spec[1].toString();
9
+ disp.els.textContent = spec[2].toString();
10
+ }
11
+
12
+ function setBlockWinner(block: HTMLElement, isWinner: boolean): void {
13
+ block.style.borderColor = isWinner ? 'var(--spec-success)' : '';
14
+ block.style.paddingTop = isWinner ? '2.5rem' : '';
15
+ block.style.boxShadow = isWinner ? '0 0 0 2px var(--spec-success)' : '';
16
+ const banner = block.querySelector('.spec-winner-banner') as HTMLElement | null;
17
+ if (banner) banner.style.transform = isWinner ? 'translateY(0)' : 'translateY(-100%)';
18
+ }
19
+
20
+ function updateResult(ins: [HTMLInputElement, HTMLInputElement], disps: [SpecDisp, SpecDisp], blocks: SpecBlocks): void {
21
+ const vA = ins[0].value.trim();
22
+ const vB = ins[1].value.trim();
23
+ const specA = calculateSpecificity(vA);
24
+ const specB = calculateSpecificity(vB);
25
+ renderSpec(disps[0], specA);
26
+ renderSpec(disps[1], specB);
27
+ const win = compareSpecificity(specA, specB);
28
+ setBlockWinner(blocks.a, win === 1 && !!vA);
29
+ setBlockWinner(blocks.b, win === -1 && !!vB);
30
+ const isTie = win === 0 && !!vA && !!vB;
31
+ blocks.tie.style.opacity = isTie ? '1' : '0';
32
+ blocks.tie.style.transform = isTie ? 'translateY(0)' : 'translateY(6px)';
33
+ }
34
+
35
+ export function setupCalculator(): void {
36
+ const inputA = document.getElementById('spec-input-a') as HTMLInputElement | null;
37
+ const inputB = document.getElementById('spec-input-b') as HTMLInputElement | null;
38
+ const blockA = document.getElementById('spec-block-a');
39
+ const blockB = document.getElementById('spec-block-b');
40
+ const tieMsg = document.getElementById('spec-tie-msg');
41
+ if (!inputA || !inputB || !blockA || !blockB || !tieMsg) return;
42
+ const dispA: SpecDisp = {
43
+ ids: document.getElementById('spec-res-a-ids')!,
44
+ cls: document.getElementById('spec-res-a-cls')!,
45
+ els: document.getElementById('spec-res-a-els')!,
46
+ };
47
+ const dispB: SpecDisp = {
48
+ ids: document.getElementById('spec-res-b-ids')!,
49
+ cls: document.getElementById('spec-res-b-cls')!,
50
+ els: document.getElementById('spec-res-b-els')!,
51
+ };
52
+ const blocks: SpecBlocks = { a: blockA, b: blockB, tie: tieMsg };
53
+ const ins: [HTMLInputElement, HTMLInputElement] = [inputA, inputB];
54
+ const compare = () => updateResult(ins, [dispA, dispB], blocks);
55
+ inputA.addEventListener('input', compare);
56
+ inputB.addEventListener('input', compare);
57
+ }
@@ -0,0 +1,11 @@
1
+ export interface CssSpecificityCalculatorUI extends Record<string, string> {
2
+ labelA: string;
3
+ labelB: string;
4
+ placeholderA: string;
5
+ placeholderB: string;
6
+ cardIds: string;
7
+ cardClasses: string;
8
+ cardElements: string;
9
+ bannerWinner: string;
10
+ msgTie: string;
11
+ }
@@ -0,0 +1,14 @@
1
+ ---
2
+ import { Bibliography as SharedBibliography } from '@jjlmoya/utils-shared';
3
+ import { cssToInlineConverter } from './index';
4
+ import type { KnownLocale } from '../../types';
5
+
6
+ interface Props {
7
+ locale?: KnownLocale;
8
+ }
9
+
10
+ const { locale = 'es' } = Astro.props;
11
+ const content = await cssToInlineConverter.i18n[locale]?.();
12
+ ---
13
+
14
+ {content && <SharedBibliography links={content.bibliography} />}