@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,292 @@
1
+ ---
2
+ import type { KnownLocale } from '../../types';
3
+ import type { CssSpecificityCalculatorUI } from './ui';
4
+
5
+ interface Props {
6
+ locale?: KnownLocale;
7
+ ui?: Record<string, unknown>;
8
+ }
9
+
10
+ const { ui } = Astro.props;
11
+ const t = (ui ?? {}) as CssSpecificityCalculatorUI;
12
+ ---
13
+
14
+ <div class="spec-root">
15
+ <div class="spec-card">
16
+ <div class="spec-comparison">
17
+ <div class="spec-vs">VS</div>
18
+
19
+ <div class="spec-block" id="spec-block-a">
20
+ <div class="spec-winner-banner" id="spec-banner-a" set:html={t.bannerWinner}></div>
21
+ <div class="spec-field">
22
+ <label for="spec-input-a" class="spec-field-label">{t.labelA}</label>
23
+ <input
24
+ type="text"
25
+ id="spec-input-a"
26
+ class="spec-input"
27
+ placeholder={t.placeholderA}
28
+ autocomplete="off"
29
+ spellcheck="false"
30
+ />
31
+ </div>
32
+ <div class="spec-counters">
33
+ <div class="spec-counter spec-counter-ids">
34
+ <span class="spec-counter-value" id="spec-res-a-ids">0</span>
35
+ <span class="spec-counter-label">{t.cardIds}</span>
36
+ </div>
37
+ <div class="spec-counter spec-counter-cls">
38
+ <span class="spec-counter-value" id="spec-res-a-cls">0</span>
39
+ <span class="spec-counter-label">{t.cardClasses}</span>
40
+ </div>
41
+ <div class="spec-counter spec-counter-els">
42
+ <span class="spec-counter-value" id="spec-res-a-els">0</span>
43
+ <span class="spec-counter-label">{t.cardElements}</span>
44
+ </div>
45
+ </div>
46
+ </div>
47
+
48
+ <div class="spec-block" id="spec-block-b">
49
+ <div class="spec-winner-banner" id="spec-banner-b" set:html={t.bannerWinner}></div>
50
+ <div class="spec-field">
51
+ <label for="spec-input-b" class="spec-field-label">{t.labelB}</label>
52
+ <input
53
+ type="text"
54
+ id="spec-input-b"
55
+ class="spec-input"
56
+ placeholder={t.placeholderB}
57
+ autocomplete="off"
58
+ spellcheck="false"
59
+ />
60
+ </div>
61
+ <div class="spec-counters">
62
+ <div class="spec-counter spec-counter-ids">
63
+ <span class="spec-counter-value" id="spec-res-b-ids">0</span>
64
+ <span class="spec-counter-label">{t.cardIds}</span>
65
+ </div>
66
+ <div class="spec-counter spec-counter-cls">
67
+ <span class="spec-counter-value" id="spec-res-b-cls">0</span>
68
+ <span class="spec-counter-label">{t.cardClasses}</span>
69
+ </div>
70
+ <div class="spec-counter spec-counter-els">
71
+ <span class="spec-counter-value" id="spec-res-b-els">0</span>
72
+ <span class="spec-counter-label">{t.cardElements}</span>
73
+ </div>
74
+ </div>
75
+ </div>
76
+ </div>
77
+
78
+ <div class="spec-tie-msg" id="spec-tie-msg" set:html={t.msgTie}></div>
79
+ </div>
80
+ </div>
81
+
82
+ <style>
83
+ .spec-root {
84
+ --spec-primary: #8b5cf6;
85
+ --spec-primary-dark: #7c3aed;
86
+ --spec-success: #10b981;
87
+ --spec-card-bg: #fff;
88
+ --spec-bg: #f8fafc;
89
+ --spec-text: #0f172a;
90
+ --spec-text-muted: #64748b;
91
+ --spec-border: #e2e8f0;
92
+
93
+ max-width: 900px;
94
+ margin: 2rem auto;
95
+ color: var(--spec-text);
96
+ }
97
+
98
+ :global(.theme-dark) .spec-root {
99
+ --spec-card-bg: #1e293b;
100
+ --spec-bg: #0f172a;
101
+ --spec-text: #f1f5f9;
102
+ --spec-text-muted: #94a3b8;
103
+ --spec-border: #334155;
104
+ }
105
+
106
+ .spec-card {
107
+ background: var(--spec-card-bg);
108
+ border: 1px solid var(--spec-border);
109
+ border-radius: 1.25rem;
110
+ box-shadow: 0 4px 24px rgba(0, 0, 0, 0.06);
111
+ padding: 1.75rem;
112
+ display: flex;
113
+ flex-direction: column;
114
+ gap: 1.25rem;
115
+ }
116
+
117
+ .spec-comparison {
118
+ display: grid;
119
+ grid-template-columns: 1fr 1fr;
120
+ gap: 1.5rem;
121
+ position: relative;
122
+ }
123
+
124
+ @media (max-width: 768px) {
125
+ .spec-comparison {
126
+ grid-template-columns: 1fr;
127
+ }
128
+ }
129
+
130
+ .spec-vs {
131
+ position: absolute;
132
+ top: 50%;
133
+ left: 50%;
134
+ transform: translate(-50%, -50%);
135
+ background: var(--spec-primary);
136
+ color: #fff;
137
+ font-weight: 800;
138
+ width: 2.75rem;
139
+ height: 2.75rem;
140
+ display: flex;
141
+ align-items: center;
142
+ justify-content: center;
143
+ border-radius: 50%;
144
+ border: 3px solid var(--spec-card-bg);
145
+ z-index: 10;
146
+ font-size: 0.85rem;
147
+ box-shadow: 0 4px 12px rgba(139, 92, 246, 0.4);
148
+ }
149
+
150
+ @media (max-width: 768px) {
151
+ .spec-vs {
152
+ display: none;
153
+ }
154
+ }
155
+
156
+ .spec-block {
157
+ background: var(--spec-bg);
158
+ border: 2px solid var(--spec-border);
159
+ border-radius: 1rem;
160
+ padding: 1.25rem;
161
+ display: flex;
162
+ flex-direction: column;
163
+ gap: 1rem;
164
+ position: relative;
165
+ overflow: hidden;
166
+ transition: border-color 0.25s, box-shadow 0.25s, padding-top 0.25s;
167
+ }
168
+
169
+ .spec-winner-banner {
170
+ position: absolute;
171
+ top: 0;
172
+ left: 0;
173
+ right: 0;
174
+ background: var(--spec-success);
175
+ color: #fff;
176
+ text-align: center;
177
+ font-weight: 700;
178
+ padding: 0.3rem;
179
+ font-size: 0.8rem;
180
+ transform: translateY(-100%);
181
+ transition: transform 0.25s;
182
+ }
183
+
184
+ .spec-field {
185
+ display: flex;
186
+ flex-direction: column;
187
+ gap: 0.4rem;
188
+ }
189
+
190
+ .spec-field-label {
191
+ font-size: 0.8rem;
192
+ font-weight: 700;
193
+ color: var(--spec-text-muted);
194
+ text-transform: uppercase;
195
+ letter-spacing: 0.06em;
196
+ }
197
+
198
+ .spec-input {
199
+ width: 100%;
200
+ padding: 0.75rem 1rem;
201
+ font-size: 1rem;
202
+ color: var(--spec-text);
203
+ background: var(--spec-card-bg);
204
+ border: 1px solid var(--spec-border);
205
+ border-radius: 0.75rem;
206
+ outline: none;
207
+ transition: border-color 0.2s, box-shadow 0.2s;
208
+ box-sizing: border-box;
209
+ }
210
+
211
+ .spec-input:focus {
212
+ border-color: var(--spec-primary);
213
+ box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.15);
214
+ }
215
+
216
+ .spec-counters {
217
+ display: grid;
218
+ grid-template-columns: repeat(3, 1fr);
219
+ gap: 0.625rem;
220
+ }
221
+
222
+ .spec-counter {
223
+ background: var(--spec-card-bg);
224
+ border: 1px solid var(--spec-border);
225
+ border-radius: 0.75rem;
226
+ padding: 0.875rem 0.5rem;
227
+ text-align: center;
228
+ display: flex;
229
+ flex-direction: column;
230
+ align-items: center;
231
+ gap: 0.375rem;
232
+ }
233
+
234
+ .spec-counter-ids {
235
+ border-bottom: 3px solid #ef4444;
236
+ }
237
+
238
+ .spec-counter-cls {
239
+ border-bottom: 3px solid #3b82f6;
240
+ }
241
+
242
+ .spec-counter-els {
243
+ border-bottom: 3px solid #10b981;
244
+ }
245
+
246
+ .spec-counter-value {
247
+ font-size: 2.25rem;
248
+ font-weight: 800;
249
+ line-height: 1;
250
+ font-variant-numeric: tabular-nums;
251
+ }
252
+
253
+ .spec-counter-ids .spec-counter-value {
254
+ color: #ef4444;
255
+ }
256
+
257
+ .spec-counter-cls .spec-counter-value {
258
+ color: #3b82f6;
259
+ }
260
+
261
+ .spec-counter-els .spec-counter-value {
262
+ color: #10b981;
263
+ }
264
+
265
+ .spec-counter-label {
266
+ font-size: 0.68rem;
267
+ text-transform: uppercase;
268
+ letter-spacing: 0.05em;
269
+ font-weight: 700;
270
+ color: var(--spec-text-muted);
271
+ }
272
+
273
+ .spec-tie-msg {
274
+ text-align: center;
275
+ padding: 0.875rem 1.25rem;
276
+ background: var(--spec-bg);
277
+ border: 1px solid var(--spec-border);
278
+ border-radius: 0.875rem;
279
+ color: var(--spec-text);
280
+ font-weight: 600;
281
+ font-size: 0.9rem;
282
+ opacity: 0;
283
+ transform: translateY(6px);
284
+ transition: opacity 0.25s, transform 0.25s;
285
+ }
286
+ </style>
287
+
288
+ <script>
289
+ import { setupCalculator } from './ui-controller';
290
+ setupCalculator();
291
+ </script>
292
+
@@ -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 = 'css-specificity-calculator';
6
+ const title = 'CSS Specificity Calculator Online. Selector Weight Visualizer';
7
+ const description =
8
+ 'Calculate the specificity and exact weight of any CSS selector. Visual tool to understand which CSS rule wins the cascade and avoid using !important.';
9
+
10
+ const faqData = [
11
+ {
12
+ question: 'What is CSS specificity?',
13
+ answer:
14
+ 'Specificity is the algorithm browsers use to decide which CSS rule applies to an element when multiple rules compete. It is represented as a three-column score (A, B, C) indicating IDs, classes/attributes/pseudo-classes, and elements/pseudo-elements respectively.',
15
+ },
16
+ {
17
+ question: 'Can a class ever beat an ID in specificity?',
18
+ answer:
19
+ 'Not directly. An ID (1,0,0) always beats any number of classes (0,N,0) because specificity has no carry-over between columns. A hundred classes (0,100,0) will never beat a single ID (1,0,0).',
20
+ },
21
+ {
22
+ question: 'What happens when two selectors have the same specificity?',
23
+ answer:
24
+ 'When two selectors have exactly the same weight, the one declared last in the CSS file wins. This is known as natural cascade order. This calculator visually alerts you when a tie occurs.',
25
+ },
26
+ {
27
+ question: 'Why is using !important considered bad practice?',
28
+ answer:
29
+ 'The !important directive breaks the natural CSS cascade by forcing a declaration over any other rule. This creates conflicts that are hard to debug in large projects. Methodologies like BEM advocate for keeping specificity flat to avoid ever needing !important.',
30
+ },
31
+ ];
32
+
33
+ const howToData = [
34
+ {
35
+ name: 'Enter the first selector',
36
+ text: 'Type Selector A in the left text field, for example: #header .nav-item > a. The IDs, Classes and Elements counters will update instantly.',
37
+ },
38
+ {
39
+ name: 'Enter the second selector',
40
+ text: 'Type Selector B in the right text field, for example: ul li.active a:hover. Watch how the weights change in real time.',
41
+ },
42
+ {
43
+ name: 'Read the result',
44
+ text: 'The calculator will highlight the winning selector block with a green banner. If both tie, a tie message will appear explaining cascade order as the tiebreaker.',
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: 'en',
80
+ };
81
+
82
+ const ui: CssSpecificityCalculatorUI = {
83
+ labelA: 'Selector A',
84
+ labelB: 'Selector B',
85
+ placeholderA: 'e.g. #header .nav-item > a',
86
+ placeholderB: 'e.g. ul li.active a:hover',
87
+ cardIds: 'IDs',
88
+ cardClasses: 'Classes / Pseudos',
89
+ cardElements: 'Elements',
90
+ bannerWinner: 'This selector wins!',
91
+ msgTie: 'Both selectors have the same weight. If they compete for the same element, the one written <strong>last</strong> in the CSS wins.',
92
+ };
93
+
94
+ export const content: ToolLocaleContent<CssSpecificityCalculatorUI> = {
95
+ slug,
96
+ title,
97
+ description,
98
+ ui,
99
+ faqTitle: 'Frequently Asked Questions',
100
+ faq: faqData,
101
+ bibliographyTitle: 'References and Documentation',
102
+ bibliography: [
103
+ {
104
+ name: 'MDN Web Docs: CSS Specificity',
105
+ url: 'https://developer.mozilla.org/en/docs/Web/CSS/Specificity',
106
+ },
107
+ {
108
+ name: 'W3C: Selectors Level 3 - Specificity',
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: 'What Is CSS Specificity?',
118
+ level: 2,
119
+ },
120
+ {
121
+ type: 'paragraph',
122
+ html: 'CSS specificity is the algorithm by which browsers decide which property values to apply to a particular element. It is essentially a mathematical score that tells the browser "how specific" a rule is.',
123
+ },
124
+ {
125
+ type: 'paragraph',
126
+ html: 'If two rules have different specificity levels, the one with the higher weight will win, regardless of the order in which they were written. If both have the same weight, the last one declared in the source code wins.',
127
+ },
128
+ {
129
+ type: 'title',
130
+ text: 'How to Calculate CSS Specificity',
131
+ level: 3,
132
+ },
133
+ {
134
+ type: 'paragraph',
135
+ html: 'Specificity is calculated based on three categories forming a three-column weight, often expressed as <strong>(A, B, C)</strong>:',
136
+ },
137
+ {
138
+ type: 'list',
139
+ items: [
140
+ '<strong>Column A (IDs):</strong> Counts the number of unique identifiers. Example: <code>#header</code> counts as 1 in column A.',
141
+ '<strong>Column B (Classes, Attributes and Pseudo-classes):</strong> Counts all classes (<code>.button</code>), attributes (<code>[type="text"]</code>) and pseudo-classes (<code>:hover</code>).',
142
+ '<strong>Column C (Elements and Pseudo-elements):</strong> Counts all HTML elements (<code>div</code>, <code>h1</code>) and pseudo-elements (<code>::before</code>).',
143
+ ],
144
+ },
145
+ {
146
+ type: 'title',
147
+ text: 'The Golden Rule: No Numeric Carry-Over',
148
+ level: 3,
149
+ },
150
+ {
151
+ type: 'tip',
152
+ html: 'A rule with specificity (0,50,0) will <strong>never</strong> be more specific than a rule (1,0,0). <strong>A single ID beats infinite classes.</strong> Columns never overflow into each other.',
153
+ },
154
+ {
155
+ type: 'title',
156
+ text: 'The Problem With !important and BEM Architecture',
157
+ level: 3,
158
+ },
159
+ {
160
+ type: 'paragraph',
161
+ html: 'The <code>!important</code> directive is an exception to specificity rules. When used, that declaration automatically overrides any other. It is considered bad practice because it destroys the natural cascade.',
162
+ },
163
+ {
164
+ type: 'paragraph',
165
+ html: 'To avoid specificity wars in large projects, methodologies like <strong>BEM</strong> advocate for using only single-depth class selectors, artificially keeping specificity flat at (0,1,0).',
166
+ },
167
+ ],
168
+ };
@@ -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 = 'calculadora-especificidad-css';
6
+ const title = 'Calculadora de Especificidad CSS Online. Visualizador de Peso';
7
+ const description =
8
+ 'Calcula la especificidad y peso exacto de cualquier selector CSS. Herramienta visual para entender qué regla CSS gana la cascada y evitar el uso de !important.';
9
+
10
+ const faqData = [
11
+ {
12
+ question: '¿Qué es la especificidad en CSS?',
13
+ answer:
14
+ 'La especificidad es el algoritmo que usan los navegadores para decidir qué regla CSS se aplica a un elemento cuando varias reglas compiten por él. Se representa como una puntuación de tres columnas (A, B, C) que indica IDs, clases/atributos/pseudoclases y elementos/pseudoelementos respectivamente.',
15
+ },
16
+ {
17
+ question: '¿Puede una clase superar a un ID en especificidad?',
18
+ answer:
19
+ 'No directamente. Un ID (1,0,0) siempre vence a cualquier número de clases (0,N,0) porque la especificidad no tiene acarreo numérico entre columnas. Cien clases (0,100,0) nunca superarán a un solo ID (1,0,0).',
20
+ },
21
+ {
22
+ question: '¿Qué ocurre cuando dos selectores tienen la misma especificidad?',
23
+ answer:
24
+ 'Cuando dos selectores tienen exactamente el mismo peso, gana el que esté declarado último en el archivo CSS. Es lo que se conoce como el orden de la cascada natural. Esta calculadora te avisa visualmente cuando se produce un empate.',
25
+ },
26
+ {
27
+ question: '¿Por qué se considera mala práctica usar !important?',
28
+ answer:
29
+ 'La directiva !important rompe la cascada natural del CSS al forzar una declaración sobre cualquier otra regla. Esto genera conflictos difíciles de depurar en proyectos grandes y hace el mantenimiento del código frustrante. Metodologías como BEM abogan por mantener la especificidad plana para evitar necesitar !important.',
30
+ },
31
+ ];
32
+
33
+ const howToData = [
34
+ {
35
+ name: 'Escribe el primer selector',
36
+ text: 'Introduce el Selector A en el campo de texto de la izquierda, por ejemplo: #header .nav-item > a. Los contadores de IDs, Clases y Elementos se actualizarán al instante.',
37
+ },
38
+ {
39
+ name: 'Escribe el segundo selector',
40
+ text: 'Introduce el Selector B en el campo de texto de la derecha, por ejemplo: ul li.active a:hover. Observa cómo cambian los pesos en tiempo real.',
41
+ },
42
+ {
43
+ name: 'Lee el resultado',
44
+ text: 'La calculadora resaltará con un banner verde el bloque del selector ganador. Si ambos empatan, aparecerá un mensaje de empate explicando el desempate por orden de cascada.',
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: 'es',
80
+ };
81
+
82
+ const ui: CssSpecificityCalculatorUI = {
83
+ labelA: 'Selector A',
84
+ labelB: 'Selector B',
85
+ placeholderA: 'ej. #header .nav-item > a',
86
+ placeholderB: 'ej. ul li.active a:hover',
87
+ cardIds: 'IDs',
88
+ cardClasses: 'Clases / Pseudos',
89
+ cardElements: 'Elementos',
90
+ bannerWinner: '¡Este selector gana!',
91
+ msgTie: 'Ambos selectores tienen el mismo peso. Si compiten por el mismo elemento, ganará el que esté escrito <strong>último</strong> en el CSS.',
92
+ };
93
+
94
+ export const content: ToolLocaleContent<CssSpecificityCalculatorUI> = {
95
+ slug,
96
+ title,
97
+ description,
98
+ ui,
99
+ faqTitle: 'Preguntas Frecuentes',
100
+ faq: faqData,
101
+ bibliographyTitle: 'Referencias y Documentación',
102
+ bibliography: [
103
+ {
104
+ name: 'MDN Web Docs: Especificidad en CSS',
105
+ url: 'https://developer.mozilla.org/es/docs/Web/CSS/Specificity',
106
+ },
107
+ {
108
+ name: 'W3C: Selectors Level 3 - Especificidad',
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é es la Especificidad en CSS?',
118
+ level: 2,
119
+ },
120
+ {
121
+ type: 'paragraph',
122
+ html: 'La especificidad en CSS es el algoritmo mediante el cual los navegadores deciden qué valores de las propiedades aplicarán a un elemento. Básicamente, es una puntuación matemática que indica al navegador "cuán específica" es una regla.',
123
+ },
124
+ {
125
+ type: 'paragraph',
126
+ html: 'Si dos reglas tienen diferentes niveles de especificidad, ganará la que tenga mayor peso, sin importar el orden en el que fueron escritas. Si ambas tienen el mismo peso, ganará la última declarada en el código fuente.',
127
+ },
128
+ {
129
+ type: 'title',
130
+ text: 'Cómo calcular la Especificidad CSS',
131
+ level: 3,
132
+ },
133
+ {
134
+ type: 'paragraph',
135
+ html: 'La especificidad se calcula en tres categorías que forman un peso de tres columnas, expresado como <strong>(A, B, C)</strong>:',
136
+ },
137
+ {
138
+ type: 'list',
139
+ items: [
140
+ '<strong>Columna A (IDs):</strong> Suma el número de identificadores únicos. Ejemplo: <code>#header</code> cuenta como 1 en la columna A.',
141
+ '<strong>Columna B (Clases, Atributos y Pseudos):</strong> Suma todas las clases (<code>.button</code>), atributos (<code>[type="text"]</code>) y pseudo-clases (<code>:hover</code>).',
142
+ '<strong>Columna C (Elementos y Pseudo-elementos):</strong> Suma todos los elementos HTML (<code>div</code>, <code>h1</code>) y pseudo-elementos (<code>::before</code>).',
143
+ ],
144
+ },
145
+ {
146
+ type: 'title',
147
+ text: 'La regla de oro: no hay acarreo numérico',
148
+ level: 3,
149
+ },
150
+ {
151
+ type: 'tip',
152
+ html: 'Una regla con especificidad (0,50,0) <strong>nunca</strong> será más específica que una regla (1,0,0). <strong>Un solo ID vence a infinitas clases.</strong> Las columnas no se trasladan entre sí.',
153
+ },
154
+ {
155
+ type: 'title',
156
+ text: 'El problema de !important y la arquitectura BEM',
157
+ level: 3,
158
+ },
159
+ {
160
+ type: 'paragraph',
161
+ html: 'La directiva <code>!important</code> es una excepción a las reglas de especificidad. Cuando la utilizas, esa declaración sobrescribe cualquier otra automáticamente. Se considera mala práctica ya que destruye la cascada natural.',
162
+ },
163
+ {
164
+ type: 'paragraph',
165
+ html: 'Para evitar guerras de especificidad en proyectos grandes, metodologías como <strong>BEM</strong> abogan por el uso exclusivo de selectores de clase a un único nivel de profundidad, manteniendo la especificidad plana en (0,1,0).',
166
+ },
167
+ ],
168
+ };